--- ndisc.c.offic Wed Apr 25 22:46:35 2001 +++ ndisc.c Sat May 5 18:33:29 2001 @@ -21,6 +21,7 @@ * Janos Farkas : kmalloc failure checks * Alexey Kuznetsov : state machine reworked * and moved to net/core. + * Pekka Savola : RFC2461 validation */ /* Set to 3 to get tracing... */ @@ -581,9 +582,9 @@ optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg); - if (skb->nh.ipv6h->hop_limit != 255) { - printk(KERN_INFO - "NDISC: fake router advertisment received\n"); + if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP RA: source address is not linklocal\n"); return; } @@ -756,13 +757,9 @@ int on_link = 0; int optlen; - if (skb->nh.ipv6h->hop_limit != 255) { - printk(KERN_WARNING "NDISC: fake ICMP redirect received\n"); - return; - } - if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) { - printk(KERN_WARNING "ICMP redirect: source address is not linklocal\n"); + if (net_ratelimit()) + printk(KERN_WARNING "ICMP redirect: source address is not linklocal\n"); return; } @@ -770,7 +767,8 @@ optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr); if (optlen < 0) { - printk(KERN_WARNING "ICMP redirect: packet too small\n"); + if (net_ratelimit()) + printk(KERN_WARNING "ICMP redirect: packet too small\n"); return; } @@ -779,14 +777,16 @@ dest = target + 1; if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) { - printk(KERN_WARNING "ICMP redirect for multicast addr\n"); + if (net_ratelimit()) + printk(KERN_WARNING "ICMP redirect for multicast addr\n"); return; } if (ipv6_addr_cmp(dest, target) == 0) { on_link = 1; } else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) { - printk(KERN_WARNING "ICMP redirect: target address is not linklocal\n"); + if (net_ratelimit()) + printk(KERN_WARNING "ICMP redirect: target address is not linklocal\n"); return; } @@ -974,8 +974,34 @@ __skb_push(skb, skb->data-skb->h.raw); + if (skb->nh.ipv6h->hop_limit != 255) { + if (net_ratelimit()) + printk(KERN_WARNING + "ICMP NDISC: fake message with non-255 Hop Limit received: %d\n", + skb->nh.ipv6h->hop_limit); + return 0; + } + + if (msg->icmph.icmp6_code != 0) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP NDISC: code is not zero\n"); + return 0; + } + switch (msg->icmph.icmp6_type) { case NDISC_NEIGHBOUR_SOLICITATION: + if (skb->nh.ipv6h->payload_len < 8+16) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP NS: packet too short\n"); + return 0; + } + + if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP NS: target address is multicast\n"); + return 0; + } + if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) { int addr_type = ipv6_addr_type(saddr); @@ -1010,7 +1036,9 @@ ipv6_addr_all_nodes(&maddr); ndisc_send_na(dev, NULL, &maddr, &ifp->addr, - ifp->idev->cnf.forwarding, 0, 1, 1); + ifp->idev->cnf.forwarding, 0, + ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, + 1); in6_ifa_put(ifp); return 0; } @@ -1032,7 +1060,9 @@ if (neigh) { ndisc_send_na(dev, neigh, saddr, &ifp->addr, - ifp->idev->cnf.forwarding, 1, inc, inc); + ifp->idev->cnf.forwarding, 1, + ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, + 1); neigh_release(neigh); } } @@ -1059,7 +1089,7 @@ if (neigh) { ndisc_send_na(dev, neigh, saddr, &msg->target, - 0, 1, 0, inc); + 0, 1, 0, 1); neigh_release(neigh); } } else { @@ -1077,11 +1107,24 @@ return 0; case NDISC_NEIGHBOUR_ADVERTISEMENT: + if (skb->nh.ipv6h->payload_len < 16+8 ) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP NA: packet too short\n"); + return 0; + } + + if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) { + if (net_ratelimit()) + printk(KERN_WARNING "NDISC NA: target address is multicast\n"); + return 0; + } + if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) && msg->icmph.icmp6_solicited) { ND_PRINTK0("NDISC: solicited NA is multicasted\n"); return 0; } + if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) { if (ifp->flags & IFA_F_TENTATIVE) { addrconf_dad_failure(ifp); @@ -1092,7 +1135,7 @@ about it. It could be misconfiguration, or an smart proxy agent tries to help us :-) */ - ND_PRINTK0("%s: someone avertise our address!\n", + ND_PRINTK0("%s: someone advertises our address!\n", ifp->idev->dev->name); in6_ifa_put(ifp); return 0; @@ -1125,12 +1168,32 @@ break; case NDISC_ROUTER_ADVERTISEMENT: + if (skb->nh.ipv6h->payload_len < 8+4+4) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP RA: packet too short\n"); + return 0; + } ndisc_router_discovery(skb); break; case NDISC_REDIRECT: + if (skb->nh.ipv6h->payload_len < 8+16+16) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP redirect: packet too short\n"); + return 0; + } ndisc_redirect_rcv(skb); break; + + case NDISC_ROUTER_SOLICITATION: + /* No RS support in the kernel, but we do some required checks */ + + if (skb->nh.ipv6h->payload_len < 8) { + if (net_ratelimit()) + printk(KERN_WARNING "ICMP RS: packet too short\n"); + return 0; + } + break; }; return 0; @@ -1228,7 +1291,7 @@ if((err = ops->create(ndisc_socket, IPPROTO_ICMPV6)) < 0) { printk(KERN_DEBUG - "Failed to initializee the NDISC control socket (err %d).\n", + "Failed to initialize the NDISC control socket (err %d).\n", err); sock_release(ndisc_socket); ndisc_socket = NULL; /* For safety. */