ndisc: Remove dev argument for ndisc_send_skb().
[deliverable/linux.git] / net / ipv6 / ndisc.c
index f2a007b7bde34d38f5b9b7f49c41ce77b136d04c..200b2b22b9ba29e49e380a1af8b92f48b0eb30dc 100644 (file)
@@ -143,16 +143,12 @@ struct neigh_table nd_tbl = {
        .gc_thresh3 =   1024,
 };
 
-static inline int ndisc_opt_addr_space(struct net_device *dev)
+static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data,
+                                 struct net_device *dev)
 {
-       return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type));
-}
-
-static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
-                                 unsigned short addr_type)
-{
-       int pad   = ndisc_addr_option_pad(addr_type);
-       int space = NDISC_OPT_SPACE(data_len + pad);
+       int pad   = ndisc_addr_option_pad(dev->type);
+       int data_len = dev->addr_len;
+       int space = ndisc_opt_addr_space(dev);
 
        opt[0] = type;
        opt[1] = space>>3;
@@ -370,6 +366,53 @@ static void pndisc_destructor(struct pneigh_entry *n)
        ipv6_dev_mc_dec(dev, &maddr);
 }
 
+static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
+                                      int len)
+{
+       int hlen = LL_RESERVED_SPACE(dev);
+       int tlen = dev->needed_tailroom;
+       struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
+       struct sk_buff *skb;
+       int err;
+
+       skb = sock_alloc_send_skb(sk,
+                                 hlen + sizeof(struct ipv6hdr) + len + tlen,
+                                 1, &err);
+       if (!skb) {
+               ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
+                         __func__, err);
+               return NULL;
+       }
+
+       skb->protocol = htons(ETH_P_IPV6);
+       skb->dev = dev;
+
+       skb_reserve(skb, hlen);
+
+       return skb;
+}
+
+static void ip6_nd_hdr(struct sk_buff *skb,
+                      const struct in6_addr *saddr,
+                      const struct in6_addr *daddr,
+                      int hop_limit, int len)
+{
+       struct ipv6hdr *hdr;
+
+       skb_reset_network_header(skb);
+       skb_put(skb, sizeof(struct ipv6hdr));
+       hdr = ipv6_hdr(skb);
+
+       ip6_flow_hdr(hdr, 0, 0);
+
+       hdr->payload_len = htons(len);
+       hdr->nexthdr = IPPROTO_ICMPV6;
+       hdr->hop_limit = hop_limit;
+
+       hdr->saddr = *saddr;
+       hdr->daddr = *daddr;
+}
+
 static struct sk_buff *ndisc_build_skb(struct net_device *dev,
                                       const struct in6_addr *daddr,
                                       const struct in6_addr *saddr,
@@ -381,10 +424,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev,
        struct sock *sk = net->ipv6.ndisc_sk;
        struct sk_buff *skb;
        struct icmp6hdr *hdr;
-       int hlen = LL_RESERVED_SPACE(dev);
-       int tlen = dev->needed_tailroom;
        int len;
-       int err;
        u8 *opt;
 
        if (!dev->addr_len)
@@ -394,18 +434,11 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev,
        if (llinfo)
                len += ndisc_opt_addr_space(dev);
 
-       skb = sock_alloc_send_skb(sk,
-                                 (MAX_HEADER + sizeof(struct ipv6hdr) +
-                                  len + hlen + tlen),
-                                 1, &err);
-       if (!skb) {
-               ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n",
-                         __func__, err);
+       skb = ndisc_alloc_skb(dev, len);
+       if (!skb)
                return NULL;
-       }
 
-       skb_reserve(skb, hlen);
-       ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+       ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, len);
 
        skb->transport_header = skb->tail;
        skb_put(skb, len);
@@ -420,8 +453,7 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev,
        }
 
        if (llinfo)
-               ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
-                                      dev->addr_len, dev->type);
+               ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, dev);
 
        hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
                                           IPPROTO_ICMPV6,
@@ -431,15 +463,14 @@ static struct sk_buff *ndisc_build_skb(struct net_device *dev,
        return skb;
 }
 
-static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
-                          struct neighbour *neigh,
+static void ndisc_send_skb(struct sk_buff *skb,
                           const struct in6_addr *daddr,
                           const struct in6_addr *saddr,
                           struct icmp6hdr *icmp6h)
 {
        struct flowi6 fl6;
        struct dst_entry *dst;
-       struct net *net = dev_net(dev);
+       struct net *net = dev_net(skb->dev);
        struct sock *sk = net->ipv6.ndisc_sk;
        struct inet6_dev *idev;
        int err;
@@ -447,8 +478,8 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
 
        type = icmp6h->icmp6_type;
 
-       icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex);
-       dst = icmp6_dst_alloc(dev, neigh, &fl6);
+       icmpv6_flow_init(sk, &fl6, type, saddr, daddr, skb->dev->ifindex);
+       dst = icmp6_dst_alloc(skb->dev, &fl6);
        if (IS_ERR(dst)) {
                kfree_skb(skb);
                return;
@@ -474,7 +505,6 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
  *     Send a Neighbour Discover packet
  */
 static void __ndisc_send(struct net_device *dev,
-                        struct neighbour *neigh,
                         const struct in6_addr *daddr,
                         const struct in6_addr *saddr,
                         struct icmp6hdr *icmp6h, const struct in6_addr *target,
@@ -486,13 +516,13 @@ static void __ndisc_send(struct net_device *dev,
        if (!skb)
                return;
 
-       ndisc_send_skb(skb, dev, neigh, daddr, saddr, icmp6h);
+       ndisc_send_skb(skb, daddr, saddr, icmp6h);
 }
 
 static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
                          const struct in6_addr *daddr,
                          const struct in6_addr *solicited_addr,
-                         int router, int solicited, int override, int inc_opt)
+                         bool router, bool solicited, bool override, bool inc_opt)
 {
        struct in6_addr tmpaddr;
        struct inet6_ifaddr *ifp;
@@ -521,8 +551,7 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
        icmp6h.icmp6_solicited = solicited;
        icmp6h.icmp6_override = override;
 
-       __ndisc_send(dev, neigh, daddr, src_addr,
-                    &icmp6h, solicited_addr,
+       __ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr,
                     inc_opt ? ND_OPT_TARGET_LL_ADDR : 0);
 }
 
@@ -563,8 +592,7 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
                saddr = &addr_buf;
        }
 
-       __ndisc_send(dev, neigh, daddr, saddr,
-                    &icmp6h, solicit,
+       __ndisc_send(dev, daddr, saddr, &icmp6h, solicit,
                     !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);
 }
 
@@ -598,8 +626,7 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
                }
        }
 #endif
-       __ndisc_send(dev, NULL, daddr, saddr,
-                    &icmp6h, NULL,
+       __ndisc_send(dev, daddr, saddr, &icmp6h, NULL,
                     send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);
 }
 
@@ -676,6 +703,11 @@ static void ndisc_recv_ns(struct sk_buff *skb)
        bool inc;
        int is_router = -1;
 
+       if (skb->len < sizeof(struct nd_msg)) {
+               ND_PRINTK(2, warn, "NS: packet too short\n");
+               return;
+       }
+
        if (ipv6_addr_is_multicast(&msg->target)) {
                ND_PRINTK(2, warn, "NS: multicast target address\n");
                return;
@@ -685,11 +717,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
         * RFC2461 7.1.1:
         * DAD has to be destined for solicited node multicast address.
         */
-       if (dad &&
-           !(daddr->s6_addr32[0] == htonl(0xff020000) &&
-             daddr->s6_addr32[1] == htonl(0x00000000) &&
-             daddr->s6_addr32[2] == htonl(0x00000001) &&
-             daddr->s6_addr [12] == 0xff )) {
+       if (dad && !ipv6_addr_is_solict_mult(daddr)) {
                ND_PRINTK(2, warn, "NS: bad DAD packet (wrong destination)\n");
                return;
        }
@@ -780,11 +808,11 @@ static void ndisc_recv_ns(struct sk_buff *skb)
        }
 
        if (is_router < 0)
-               is_router = !!idev->cnf.forwarding;
+               is_router = idev->cnf.forwarding;
 
        if (dad) {
                ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &msg->target,
-                             is_router, 0, (ifp != NULL), 1);
+                             !!is_router, false, (ifp != NULL), true);
                goto out;
        }
 
@@ -805,8 +833,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
                             NEIGH_UPDATE_F_OVERRIDE);
        if (neigh || !dev->header_ops) {
                ndisc_send_na(dev, neigh, saddr, &msg->target,
-                             is_router,
-                             1, (ifp != NULL && inc), inc);
+                             !!is_router,
+                             true, (ifp != NULL && inc), inc);
                if (neigh)
                        neigh_release(neigh);
        }
@@ -1314,6 +1342,12 @@ out:
 
 static void ndisc_redirect_rcv(struct sk_buff *skb)
 {
+       u8 *hdr;
+       struct ndisc_options ndopts;
+       struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
+       u32 ndoptlen = skb->tail - (skb->transport_header +
+                                   offsetof(struct rd_msg, opt));
+
 #ifdef CONFIG_IPV6_NDISC_NODETYPE
        switch (skb->ndisc_nodetype) {
        case NDISC_NODETYPE_HOST:
@@ -1330,26 +1364,48 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
                return;
        }
 
+       if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
+               return;
+
+       if (!ndopts.nd_opts_rh)
+               return;
+
+       hdr = (u8 *)ndopts.nd_opts_rh;
+       hdr += 8;
+       if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
+               return;
+
        icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
 }
 
+static u8 *ndisc_fill_redirect_hdr_option(u8 *opt, struct sk_buff *orig_skb,
+                                         int rd_len)
+{
+       memset(opt, 0, 8);
+       *(opt++) = ND_OPT_REDIRECT_HDR;
+       *(opt++) = (rd_len >> 3);
+       opt += 6;
+
+       memcpy(opt, ipv6_hdr(orig_skb), rd_len - 8);
+
+       return opt + rd_len - 8;
+}
+
 void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
 {
        struct net_device *dev = skb->dev;
        struct net *net = dev_net(dev);
        struct sock *sk = net->ipv6.ndisc_sk;
-       int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
+       int len = sizeof(struct rd_msg);
        struct inet_peer *peer;
        struct sk_buff *buff;
-       struct icmp6hdr *icmph;
+       struct rd_msg *msg;
        struct in6_addr saddr_buf;
-       struct in6_addr *addrp;
        struct rt6_info *rt;
        struct dst_entry *dst;
        struct inet6_dev *idev;
        struct flowi6 fl6;
        u8 *opt;
-       int hlen, tlen;
        int rd_len;
        int err;
        u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
@@ -1419,63 +1475,46 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
        rd_len &= ~0x7;
        len += rd_len;
 
-       hlen = LL_RESERVED_SPACE(dev);
-       tlen = dev->needed_tailroom;
-       buff = sock_alloc_send_skb(sk,
-                                  (MAX_HEADER + sizeof(struct ipv6hdr) +
-                                   len + hlen + tlen),
-                                  1, &err);
-       if (buff == NULL) {
-               ND_PRINTK(0, err,
-                         "Redirect: %s failed to allocate an skb, err=%d\n",
-                         __func__, err);
+       buff = ndisc_alloc_skb(dev, len);
+       if (!buff)
                goto release;
-       }
 
-       skb_reserve(buff, hlen);
-       ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
-                  IPPROTO_ICMPV6, len);
+       ip6_nd_hdr(buff, &saddr_buf, &ipv6_hdr(skb)->saddr,
+                  inet6_sk(sk)->hop_limit, len);
 
        skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);
        skb_put(buff, len);
-       icmph = icmp6_hdr(buff);
+       msg = (struct rd_msg *)icmp6_hdr(buff);
 
-       memset(icmph, 0, sizeof(struct icmp6hdr));
-       icmph->icmp6_type = NDISC_REDIRECT;
+       memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
+       msg->icmph.icmp6_type = NDISC_REDIRECT;
 
        /*
         *      copy target and destination addresses
         */
 
-       addrp = (struct in6_addr *)(icmph + 1);
-       *addrp = *target;
-       addrp++;
-       *addrp = ipv6_hdr(skb)->daddr;
+       msg->target = *target;
+       msg->dest = ipv6_hdr(skb)->daddr;
 
-       opt = (u8*) (addrp + 1);
+       opt = msg->opt;
 
        /*
         *      include target_address option
         */
 
        if (ha)
-               opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
-                                            dev->addr_len, dev->type);
+               opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, dev);
 
        /*
         *      build redirect option and copy skb over to the new packet.
         */
 
-       memset(opt, 0, 8);
-       *(opt++) = ND_OPT_REDIRECT_HDR;
-       *(opt++) = (rd_len >> 3);
-       opt += 6;
-
-       memcpy(opt, ipv6_hdr(skb), rd_len - 8);
+       if (rd_len)
+               opt = ndisc_fill_redirect_hdr_option(opt, skb, rd_len);
 
-       icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
-                                            len, IPPROTO_ICMPV6,
-                                            csum_partial(icmph, len, 0));
+       msg->icmph.icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,
+                                                len, IPPROTO_ICMPV6,
+                                                csum_partial(msg, len, 0));
 
        skb_dst_set(buff, dst);
        rcu_read_lock();
@@ -1505,7 +1544,7 @@ int ndisc_rcv(struct sk_buff *skb)
 {
        struct nd_msg *msg;
 
-       if (!pskb_may_pull(skb, skb->len))
+       if (skb_linearize(skb))
                return 0;
 
        msg = (struct nd_msg *)skb_transport_header(skb);
This page took 0.038565 seconds and 5 git commands to generate.