ipv4: Add a socket release callback for datagram sockets
authorSteffen Klassert <steffen.klassert@secunet.com>
Mon, 21 Jan 2013 02:00:03 +0000 (02:00 +0000)
committerDavid S. Miller <davem@davemloft.net>
Mon, 21 Jan 2013 19:17:05 +0000 (14:17 -0500)
This implements a socket release callback function to check
if the socket cached route got invalid during the time
we owned the socket. The function is used from udp, raw
and ping sockets.

Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ip.h
net/ipv4/datagram.c
net/ipv4/ping.c
net/ipv4/raw.c
net/ipv4/udp.c

index 0707fb9..a68f838 100644 (file)
@@ -143,6 +143,8 @@ static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
 extern int             ip4_datagram_connect(struct sock *sk, 
                                             struct sockaddr *uaddr, int addr_len);
 
+extern void ip4_datagram_release_cb(struct sock *sk);
+
 struct ip_reply_arg {
        struct kvec iov[1];   
        int         flags;
index 424fafb..b28e863 100644 (file)
@@ -85,3 +85,28 @@ out:
        return err;
 }
 EXPORT_SYMBOL(ip4_datagram_connect);
+
+void ip4_datagram_release_cb(struct sock *sk)
+{
+       const struct inet_sock *inet = inet_sk(sk);
+       const struct ip_options_rcu *inet_opt;
+       __be32 daddr = inet->inet_daddr;
+       struct flowi4 fl4;
+       struct rtable *rt;
+
+       if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
+               return;
+
+       rcu_read_lock();
+       inet_opt = rcu_dereference(inet->inet_opt);
+       if (inet_opt && inet_opt->opt.srr)
+               daddr = inet_opt->opt.faddr;
+       rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr,
+                                  inet->inet_saddr, inet->inet_dport,
+                                  inet->inet_sport, sk->sk_protocol,
+                                  RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
+       if (!IS_ERR(rt))
+               __sk_dst_set(sk, &rt->dst);
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
index 8f3d054..6f9c072 100644 (file)
@@ -738,6 +738,7 @@ struct proto ping_prot = {
        .recvmsg =      ping_recvmsg,
        .bind =         ping_bind,
        .backlog_rcv =  ping_queue_rcv_skb,
+       .release_cb =   ip4_datagram_release_cb,
        .hash =         ping_v4_hash,
        .unhash =       ping_v4_unhash,
        .get_port =     ping_v4_get_port,
index 73d1e4d..6f08991 100644 (file)
@@ -894,6 +894,7 @@ struct proto raw_prot = {
        .recvmsg           = raw_recvmsg,
        .bind              = raw_bind,
        .backlog_rcv       = raw_rcv_skb,
+       .release_cb        = ip4_datagram_release_cb,
        .hash              = raw_hash_sk,
        .unhash            = raw_unhash_sk,
        .obj_size          = sizeof(struct raw_sock),
index 79c8dbe..1f4d405 100644 (file)
@@ -1952,6 +1952,7 @@ struct proto udp_prot = {
        .recvmsg           = udp_recvmsg,
        .sendpage          = udp_sendpage,
        .backlog_rcv       = __udp_queue_rcv_skb,
+       .release_cb        = ip4_datagram_release_cb,
        .hash              = udp_lib_hash,
        .unhash            = udp_lib_unhash,
        .rehash            = udp_v4_rehash,