patch-2.4.5 linux/net/ipv6/ndisc.c
Next file: linux/net/ipv6/netfilter/ip6_tables.c
Previous file: linux/net/ipv6/addrconf.c
Back to the patch index
Back to the overall index
- Lines: 318
- Date:
Wed May 16 10:31:27 2001
- Orig file:
v2.4.4/linux/net/ipv6/ndisc.c
- Orig date:
Thu Apr 26 22:17:26 2001
diff -u --recursive --new-file v2.4.4/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
@@ -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... */
@@ -242,14 +243,8 @@
if (dev == NULL || __in6_dev_get(dev) == NULL)
return -EINVAL;
-#ifndef CONFIG_IPV6_NO_PB
- addrconf_addr_solict_mult_old(addr, &maddr);
+ addrconf_addr_solict_mult(addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
-#endif
-#ifdef CONFIG_IPV6_EUI64
- addrconf_addr_solict_mult_new(addr, &maddr);
- ipv6_dev_mc_inc(dev, &maddr);
-#endif
return 0;
}
@@ -261,14 +256,8 @@
if (dev == NULL || __in6_dev_get(dev) == NULL)
return;
-#ifndef CONFIG_IPV6_NO_PB
- addrconf_addr_solict_mult_old(addr, &maddr);
- ipv6_dev_mc_dec(dev, &maddr);
-#endif
-#ifdef CONFIG_IPV6_EUI64
- addrconf_addr_solict_mult_new(addr, &maddr);
+ addrconf_addr_solict_mult(addr, &maddr);
ipv6_dev_mc_dec(dev, &maddr);
-#endif
}
@@ -393,6 +382,12 @@
int err;
int send_llinfo;
+ if (saddr == NULL) {
+ if (ipv6_get_lladdr(dev, &addr_buf))
+ return;
+ saddr = &addr_buf;
+ }
+
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
send_llinfo = dev->addr_len && ipv6_addr_type(saddr) != IPV6_ADDR_ANY;
if (send_llinfo)
@@ -405,14 +400,6 @@
return;
}
- if (saddr == NULL) {
- if (ipv6_get_lladdr(dev, &addr_buf)) {
- kfree_skb(skb);
- return;
- }
- saddr = &addr_buf;
- }
-
if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
kfree_skb(skb);
return;
@@ -550,14 +537,8 @@
neigh_app_ns(neigh);
#endif
} else {
-#ifdef CONFIG_IPV6_EUI64
- addrconf_addr_solict_mult_new(target, &mcaddr);
+ addrconf_addr_solict_mult(target, &mcaddr);
ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
-#endif
-#ifndef CONFIG_IPV6_NO_PB
- addrconf_addr_solict_mult_old(target, &mcaddr);
- ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);
-#endif
}
}
@@ -581,9 +562,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 +737,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 +747,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 +757,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;
}
@@ -798,6 +778,11 @@
return;
}
+ /* XXX: RFC2461 8.1:
+ * The IP source address of the Redirect MUST be the same as the current
+ * first-hop router for the specified ICMP Destination Address.
+ */
+
/* passed validation tests */
/*
@@ -974,8 +959,52 @@
__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;
+ }
+
+ /* XXX: RFC2461 Validation of [all ndisc messages]:
+ * All included ndisc options MUST be of non-zero length
+ * (Some checking in ndisc_find_option)
+ */
+
switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION:
+ /* XXX: import nd_neighbor_solicit from glibc netinet/icmp6.h */
+ 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;
+ }
+
+ /* XXX: RFC2461 7.1.1:
+ * If the IP source address is the unspecified address, there
+ * MUST NOT be source link-layer address option in the message.
+ *
+ * NOTE! Linux kernel < 2.4.4 broke this rule.
+ */
+
+ /* XXX: RFC2461 7.1.1:
+ * If the IP source address is the unspecified address, the IP
+ * destination address MUST be a solicited-node multicast address.
+ */
+
if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) {
int addr_type = ipv6_addr_type(saddr);
@@ -1010,7 +1039,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 +1063,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 +1092,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 +1110,25 @@
return 0;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
+ /* XXX: import nd_neighbor_advert from glibc netinet/icmp6.h */
+ 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 +1139,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 +1172,35 @@
break;
case NDISC_ROUTER_ADVERTISEMENT:
+ /* XXX: import nd_router_advert from glibc netinet/icmp6.h */
+ 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:
+ /* XXX: import nd_redirect from glibc netinet/icmp6.h */
+ 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 */
+
+ /* XXX: import nd_router_solicit from glibc netinet/icmp6.h */
+ 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 +1298,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. */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)