]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/ipv4/route.c
route: fix ICMP redirect validation
[mirror_ubuntu-bionic-kernel.git] / net / ipv4 / route.c
index 1730689f560e67cd3e2142834d805216134202c9..41557e2bb56ee6b87bf0f657beac87fa3d3caf80 100644 (file)
 #include <linux/sysctl.h>
 #endif
 #include <net/atmclip.h>
+#include <net/secure_seq.h>
 
 #define RT_FL_TOS(oldflp4) \
     ((u32)(oldflp4->flowi4_tos & (IPTOS_RT_MASK | RTO_ONLINK)))
@@ -721,7 +722,7 @@ static inline bool compare_hash_inputs(const struct rtable *rt1,
 {
        return ((((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) |
                ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) |
-               (rt1->rt_iif ^ rt2->rt_iif)) == 0);
+               (rt1->rt_route_iif ^ rt2->rt_route_iif)) == 0);
 }
 
 static inline int compare_keys(struct rtable *rt1, struct rtable *rt2)
@@ -730,8 +731,8 @@ static inline int compare_keys(struct rtable *rt1, struct rtable *rt2)
                ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) |
                (rt1->rt_mark ^ rt2->rt_mark) |
                (rt1->rt_key_tos ^ rt2->rt_key_tos) |
-               (rt1->rt_oif ^ rt2->rt_oif) |
-               (rt1->rt_iif ^ rt2->rt_iif)) == 0;
+               (rt1->rt_route_iif ^ rt2->rt_route_iif) |
+               (rt1->rt_oif ^ rt2->rt_oif)) == 0;
 }
 
 static inline int compare_netns(struct rtable *rt1, struct rtable *rt2)
@@ -1308,7 +1309,12 @@ static void rt_del(unsigned hash, struct rtable *rt)
 void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
                    __be32 saddr, struct net_device *dev)
 {
+       int s, i;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
+       struct rtable *rt;
+       __be32 skeys[2] = { saddr, 0 };
+       int    ikeys[2] = { dev->ifindex, 0 };
+       struct flowi4 fl4;
        struct inet_peer *peer;
        struct net *net;
 
@@ -1331,13 +1337,34 @@ void ip_rt_redirect(__be32 old_gw, __be32 daddr, __be32 new_gw,
                        goto reject_redirect;
        }
 
-       peer = inet_getpeer_v4(daddr, 1);
-       if (peer) {
-               peer->redirect_learned.a4 = new_gw;
+       memset(&fl4, 0, sizeof(fl4));
+       fl4.daddr = daddr;
+       for (s = 0; s < 2; s++) {
+               for (i = 0; i < 2; i++) {
+                       fl4.flowi4_oif = ikeys[i];
+                       fl4.saddr = skeys[s];
+                       rt = __ip_route_output_key(net, &fl4);
+                       if (IS_ERR(rt))
+                               continue;
 
-               inet_putpeer(peer);
+                       if (rt->dst.error || rt->dst.dev != dev ||
+                           rt->rt_gateway != old_gw) {
+                               ip_rt_put(rt);
+                               continue;
+                       }
 
-               atomic_inc(&__rt_peer_genid);
+                       if (!rt->peer)
+                               rt_bind_peer(rt, rt->rt_dst, 1);
+
+                       peer = rt->peer;
+                       if (peer) {
+                               peer->redirect_learned.a4 = new_gw;
+                               atomic_inc(&__rt_peer_genid);
+                       }
+
+                       ip_rt_put(rt);
+                       return;
+               }
        }
        return;
 
@@ -1628,16 +1655,18 @@ static int check_peer_redir(struct dst_entry *dst, struct inet_peer *peer)
 {
        struct rtable *rt = (struct rtable *) dst;
        __be32 orig_gw = rt->rt_gateway;
-       struct neighbour *n;
+       struct neighbour *n, *old_n;
 
        dst_confirm(&rt->dst);
 
-       neigh_release(dst_get_neighbour(&rt->dst));
-       dst_set_neighbour(&rt->dst, NULL);
-
        rt->rt_gateway = peer->redirect_learned.a4;
-       rt_bind_neighbour(rt);
-       n = dst_get_neighbour(&rt->dst);
+
+       n = ipv4_neigh_lookup(&rt->dst, &rt->rt_gateway);
+       if (IS_ERR(n))
+               return PTR_ERR(n);
+       old_n = xchg(&rt->dst._neighbour, n);
+       if (old_n)
+               neigh_release(old_n);
        if (!n || !(n->nud_state & NUD_VALID)) {
                if (n)
                        neigh_event_send(n, NULL);
@@ -2317,8 +2346,7 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr,
             rth = rcu_dereference(rth->dst.rt_next)) {
                if ((((__force u32)rth->rt_key_dst ^ (__force u32)daddr) |
                     ((__force u32)rth->rt_key_src ^ (__force u32)saddr) |
-                    (rth->rt_iif ^ iif) |
-                    rth->rt_oif |
+                    (rth->rt_route_iif ^ iif) |
                     (rth->rt_key_tos ^ tos)) == 0 &&
                    rth->rt_mark == skb->mark &&
                    net_eq(dev_net(rth->dst.dev), net) &&