Skip to content

Commit

Permalink
rxrpc: Fix firewall route keepalive
Browse files Browse the repository at this point in the history
Fix the firewall route keepalive part of AF_RXRPC which is currently
function incorrectly by replying to VERSION REPLY packets from the server
with VERSION REQUEST packets.

Instead, send VERSION REPLY packets to the peers of service connections to
act as keep-alives 20s after the latest packet was transmitted to that
peer.

Also, just discard VERSION REPLY packets rather than replying to them.

Signed-off-by: David Howells <dhowells@redhat.com>
  • Loading branch information
dhowells committed Mar 30, 2018
1 parent c0b6ede commit ace45be
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 4 deletions.
4 changes: 4 additions & 0 deletions net/rxrpc/af_rxrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@ static __poll_t rxrpc_poll(struct file *file, struct socket *sock,
static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct rxrpc_net *rxnet;
struct rxrpc_sock *rx;
struct sock *sk;

Expand Down Expand Up @@ -801,6 +802,9 @@ static int rxrpc_create(struct net *net, struct socket *sock, int protocol,
rwlock_init(&rx->call_lock);
memset(&rx->srx, 0, sizeof(rx->srx));

rxnet = rxrpc_net(sock_net(&rx->sk));
timer_reduce(&rxnet->peer_keepalive_timer, jiffies + 1);

_leave(" = 0 [%p]", rx);
return 0;
}
Expand Down
14 changes: 13 additions & 1 deletion net/rxrpc/ar-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,16 @@ struct rxrpc_net {
struct list_head local_endpoints;
struct mutex local_mutex; /* Lock for ->local_endpoints */

spinlock_t peer_hash_lock; /* Lock for ->peer_hash */
DECLARE_HASHTABLE (peer_hash, 10);
spinlock_t peer_hash_lock; /* Lock for ->peer_hash */

#define RXRPC_KEEPALIVE_TIME 20 /* NAT keepalive time in seconds */
u8 peer_keepalive_cursor;
ktime_t peer_keepalive_base;
struct hlist_head peer_keepalive[RXRPC_KEEPALIVE_TIME + 1];
struct hlist_head peer_keepalive_new;
struct timer_list peer_keepalive_timer;
struct work_struct peer_keepalive_work;
};

/*
Expand Down Expand Up @@ -285,6 +293,8 @@ struct rxrpc_peer {
struct hlist_head error_targets; /* targets for net error distribution */
struct work_struct error_distributor;
struct rb_root service_conns; /* Service connections */
struct hlist_node keepalive_link; /* Link in net->peer_keepalive[] */
time64_t last_tx_at; /* Last time packet sent here */
seqlock_t service_conn_lock;
spinlock_t lock; /* access lock */
unsigned int if_mtu; /* interface MTU for this peer */
Expand Down Expand Up @@ -1026,6 +1036,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *, bool, rxrpc_serial_t *);
int rxrpc_send_abort_packet(struct rxrpc_call *);
int rxrpc_send_data_packet(struct rxrpc_call *, struct sk_buff *, bool);
void rxrpc_reject_packets(struct rxrpc_local *);
void rxrpc_send_keepalive(struct rxrpc_peer *);

/*
* peer_event.c
Expand All @@ -1034,6 +1045,7 @@ void rxrpc_error_report(struct sock *);
void rxrpc_peer_error_distributor(struct work_struct *);
void rxrpc_peer_add_rtt(struct rxrpc_call *, enum rxrpc_rtt_rx_trace,
rxrpc_serial_t, rxrpc_serial_t, ktime_t, ktime_t);
void rxrpc_peer_keepalive_worker(struct work_struct *);

/*
* peer_object.c
Expand Down
3 changes: 3 additions & 0 deletions net/rxrpc/conn_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
}

kernel_sendmsg(conn->params.local->socket, &msg, iov, ioc, len);
conn->params.peer->last_tx_at = ktime_get_real();
_leave("");
return;
}
Expand Down Expand Up @@ -239,6 +240,8 @@ static int rxrpc_abort_connection(struct rxrpc_connection *conn,
return -EAGAIN;
}

conn->params.peer->last_tx_at = ktime_get_real();

_leave(" = 0");
return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions net/rxrpc/input.c
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,8 @@ void rxrpc_data_ready(struct sock *udp_sk)

switch (sp->hdr.type) {
case RXRPC_PACKET_TYPE_VERSION:
if (!(sp->hdr.flags & RXRPC_CLIENT_INITIATED))
goto discard;
rxrpc_post_packet_to_local(local, skb);
goto out;

Expand Down
21 changes: 20 additions & 1 deletion net/rxrpc/net_ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,22 @@ static void rxrpc_service_conn_reap_timeout(struct timer_list *timer)
rxrpc_queue_work(&rxnet->service_conn_reaper);
}

static void rxrpc_peer_keepalive_timeout(struct timer_list *timer)
{
struct rxrpc_net *rxnet =
container_of(timer, struct rxrpc_net, peer_keepalive_timer);

if (rxnet->live)
rxrpc_queue_work(&rxnet->peer_keepalive_work);
}

/*
* Initialise a per-network namespace record.
*/
static __net_init int rxrpc_init_net(struct net *net)
{
struct rxrpc_net *rxnet = rxrpc_net(net);
int ret;
int ret, i;

rxnet->live = true;
get_random_bytes(&rxnet->epoch, sizeof(rxnet->epoch));
Expand Down Expand Up @@ -70,8 +79,16 @@ static __net_init int rxrpc_init_net(struct net *net)

INIT_LIST_HEAD(&rxnet->local_endpoints);
mutex_init(&rxnet->local_mutex);

hash_init(rxnet->peer_hash);
spin_lock_init(&rxnet->peer_hash_lock);
for (i = 0; i < ARRAY_SIZE(rxnet->peer_keepalive); i++)
INIT_HLIST_HEAD(&rxnet->peer_keepalive[i]);
INIT_HLIST_HEAD(&rxnet->peer_keepalive_new);
timer_setup(&rxnet->peer_keepalive_timer,
rxrpc_peer_keepalive_timeout, 0);
INIT_WORK(&rxnet->peer_keepalive_work, rxrpc_peer_keepalive_worker);
rxnet->peer_keepalive_base = ktime_add(ktime_get_real(), NSEC_PER_SEC);

ret = -ENOMEM;
rxnet->proc_net = proc_net_mkdir(net, "rxrpc", net->proc_net);
Expand All @@ -95,6 +112,8 @@ static __net_exit void rxrpc_exit_net(struct net *net)
struct rxrpc_net *rxnet = rxrpc_net(net);

rxnet->live = false;
del_timer_sync(&rxnet->peer_keepalive_timer);
cancel_work_sync(&rxnet->peer_keepalive_work);
rxrpc_destroy_all_calls(rxnet);
rxrpc_destroy_all_connections(rxnet);
rxrpc_destroy_all_locals(rxnet);
Expand Down
59 changes: 58 additions & 1 deletion net/rxrpc/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ struct rxrpc_abort_buffer {
__be32 abort_code;
};

static const char rxrpc_keepalive_string[] = "";

/*
* Arrange for a keepalive ping a certain time after we last transmitted. This
* lets the far side know we're still interested in this call and helps keep
Expand Down Expand Up @@ -122,6 +124,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
struct kvec iov[2];
rxrpc_serial_t serial;
rxrpc_seq_t hard_ack, top;
ktime_t now;
size_t len, n;
int ret;
u8 reason;
Expand Down Expand Up @@ -203,8 +206,10 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, bool ping,
}

ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len);
now = ktime_get_real();
if (ping)
call->ping_time = ktime_get_real();
call->ping_time = now;
conn->params.peer->last_tx_at = ktime_get_real();

if (call->state < RXRPC_CALL_COMPLETE) {
if (ret < 0) {
Expand Down Expand Up @@ -288,6 +293,7 @@ int rxrpc_send_abort_packet(struct rxrpc_call *call)

ret = kernel_sendmsg(conn->params.local->socket,
&msg, iov, 1, sizeof(pkt));
conn->params.peer->last_tx_at = ktime_get_real();

rxrpc_put_connection(conn);
return ret;
Expand Down Expand Up @@ -378,6 +384,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb,
* message and update the peer record
*/
ret = kernel_sendmsg(conn->params.local->socket, &msg, iov, 2, len);
conn->params.peer->last_tx_at = ktime_get_real();

up_read(&conn->params.local->defrag_sem);
if (ret == -EMSGSIZE)
Expand Down Expand Up @@ -429,6 +436,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb,
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg,
iov, 2, len);
conn->params.peer->last_tx_at = ktime_get_real();

opt = IP_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket, SOL_IP,
Expand All @@ -446,6 +454,7 @@ int rxrpc_send_data_packet(struct rxrpc_call *call, struct sk_buff *skb,
if (ret == 0) {
ret = kernel_sendmsg(conn->params.local->socket, &msg,
iov, 2, len);
conn->params.peer->last_tx_at = ktime_get_real();

opt = IPV6_PMTUDISC_DO;
kernel_setsockopt(conn->params.local->socket,
Expand Down Expand Up @@ -515,3 +524,51 @@ void rxrpc_reject_packets(struct rxrpc_local *local)

_leave("");
}

/*
* Send a VERSION reply to a peer as a keepalive.
*/
void rxrpc_send_keepalive(struct rxrpc_peer *peer)
{
struct rxrpc_wire_header whdr;
struct msghdr msg;
struct kvec iov[2];
size_t len;
int ret;

_enter("");

msg.msg_name = &peer->srx.transport;
msg.msg_namelen = peer->srx.transport_len;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;

whdr.epoch = htonl(peer->local->rxnet->epoch);
whdr.cid = 0;
whdr.callNumber = 0;
whdr.seq = 0;
whdr.serial = 0;
whdr.type = RXRPC_PACKET_TYPE_VERSION; /* Not client-initiated */
whdr.flags = RXRPC_LAST_PACKET;
whdr.userStatus = 0;
whdr.securityIndex = 0;
whdr._rsvd = 0;
whdr.serviceId = 0;

iov[0].iov_base = &whdr;
iov[0].iov_len = sizeof(whdr);
iov[1].iov_base = (char *)rxrpc_keepalive_string;
iov[1].iov_len = sizeof(rxrpc_keepalive_string);

len = iov[0].iov_len + iov[1].iov_len;

_proto("Tx VERSION (keepalive)");

ret = kernel_sendmsg(peer->local->socket, &msg, iov, 2, len);
if (ret < 0)
_debug("sendmsg failed: %d", ret);

peer->last_tx_at = ktime_get_real();
_leave("");
}
96 changes: 96 additions & 0 deletions net/rxrpc/peer_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,99 @@ void rxrpc_peer_add_rtt(struct rxrpc_call *call, enum rxrpc_rtt_rx_trace why,
trace_rxrpc_rtt_rx(call, why, send_serial, resp_serial, rtt,
usage, avg);
}

/*
* Perform keep-alive pings with VERSION packets to keep any NAT alive.
*/
void rxrpc_peer_keepalive_worker(struct work_struct *work)
{
struct rxrpc_net *rxnet =
container_of(work, struct rxrpc_net, peer_keepalive_work);
struct rxrpc_peer *peer;
unsigned long delay;
ktime_t base, now = ktime_get_real();
s64 diff;
u8 cursor, slot;

base = rxnet->peer_keepalive_base;
cursor = rxnet->peer_keepalive_cursor;

_enter("%u,%lld", cursor, ktime_sub(now, base));

next_bucket:
diff = ktime_to_ns(ktime_sub(now, base));
if (diff < 0)
goto resched;

_debug("at %u", cursor);
spin_lock_bh(&rxnet->peer_hash_lock);
next_peer:
if (!rxnet->live) {
spin_unlock_bh(&rxnet->peer_hash_lock);
goto out;
}

/* Everything in the bucket at the cursor is processed this second; the
* bucket at cursor + 1 goes now + 1s and so on...
*/
if (hlist_empty(&rxnet->peer_keepalive[cursor])) {
if (hlist_empty(&rxnet->peer_keepalive_new)) {
spin_unlock_bh(&rxnet->peer_hash_lock);
goto emptied_bucket;
}

hlist_move_list(&rxnet->peer_keepalive_new,
&rxnet->peer_keepalive[cursor]);
}

peer = hlist_entry(rxnet->peer_keepalive[cursor].first,
struct rxrpc_peer, keepalive_link);
hlist_del_init(&peer->keepalive_link);
if (!rxrpc_get_peer_maybe(peer))
goto next_peer;

spin_unlock_bh(&rxnet->peer_hash_lock);

_debug("peer %u {%pISp}", peer->debug_id, &peer->srx.transport);

recalc:
diff = ktime_divns(ktime_sub(peer->last_tx_at, base), NSEC_PER_SEC);
if (diff < -30 || diff > 30)
goto send; /* LSW of 64-bit time probably wrapped on 32-bit */
diff += RXRPC_KEEPALIVE_TIME - 1;
if (diff < 0)
goto send;

slot = (diff > RXRPC_KEEPALIVE_TIME - 1) ? RXRPC_KEEPALIVE_TIME - 1 : diff;
if (slot == 0)
goto send;

/* A transmission to this peer occurred since last we examined it so
* put it into the appropriate future bucket.
*/
slot = (slot + cursor) % ARRAY_SIZE(rxnet->peer_keepalive);
spin_lock_bh(&rxnet->peer_hash_lock);
hlist_add_head(&peer->keepalive_link, &rxnet->peer_keepalive[slot]);
rxrpc_put_peer(peer);
goto next_peer;

send:
rxrpc_send_keepalive(peer);
now = ktime_get_real();
goto recalc;

emptied_bucket:
cursor++;
if (cursor >= ARRAY_SIZE(rxnet->peer_keepalive))
cursor = 0;
base = ktime_add_ns(base, NSEC_PER_SEC);
goto next_bucket;

resched:
rxnet->peer_keepalive_base = base;
rxnet->peer_keepalive_cursor = cursor;
delay = nsecs_to_jiffies(-diff) + 1;
timer_reduce(&rxnet->peer_keepalive_timer, jiffies + delay);
out:
_leave("");
}
7 changes: 6 additions & 1 deletion net/rxrpc/peer_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *local,
if (!peer) {
peer = prealloc;
hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
hlist_add_head(&peer->keepalive_link, &rxnet->peer_keepalive_new);
}

spin_unlock(&rxnet->peer_hash_lock);
Expand Down Expand Up @@ -363,9 +364,12 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
if (peer && !rxrpc_get_peer_maybe(peer))
peer = NULL;
if (!peer)
if (!peer) {
hash_add_rcu(rxnet->peer_hash,
&candidate->hash_link, hash_key);
hlist_add_head(&candidate->keepalive_link,
&rxnet->peer_keepalive_new);
}

spin_unlock_bh(&rxnet->peer_hash_lock);

Expand All @@ -392,6 +396,7 @@ void __rxrpc_put_peer(struct rxrpc_peer *peer)

spin_lock_bh(&rxnet->peer_hash_lock);
hash_del_rcu(&peer->hash_link);
hlist_del_init(&peer->keepalive_link);
spin_unlock_bh(&rxnet->peer_hash_lock);

kfree_rcu(peer, rcu);
Expand Down
2 changes: 2 additions & 0 deletions net/rxrpc/rxkad.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn)
return -EAGAIN;
}

conn->params.peer->last_tx_at = ktime_get_real();
_leave(" = 0");
return 0;
}
Expand Down Expand Up @@ -722,6 +723,7 @@ static int rxkad_send_response(struct rxrpc_connection *conn,
return -EAGAIN;
}

conn->params.peer->last_tx_at = ktime_get_real();
_leave(" = 0");
return 0;
}
Expand Down

0 comments on commit ace45be

Please sign in to comment.