Commit fc1061be authored by Hideaki Yoshifuji's avatar Hideaki Yoshifuji

[IPV6]: Split ndisc_rcv into helper functions.

parent c3bb1361
......@@ -588,6 +588,249 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
}
}
void ndisc_recv_ns(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
u8 *lladdr = NULL;
int lladdrlen = 0;
u32 ndoptlen = skb->tail - msg->opt;
struct ndisc_options ndopts;
struct net_device *dev = skb->dev;
struct inet6_ifaddr *ifp;
struct neighbour *neigh;
if (skb->len < sizeof(struct nd_msg)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: packet too short\n");
return;
}
if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: target address is multicast\n");
return;
}
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
return;
}
if (ndopts.nd_opts_src_lladdr) {
lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: bad lladdr length.\n");
return;
}
}
/* 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);
if (ifp->flags & IFA_F_TENTATIVE) {
/* Address is tentative. If the source
is unspecified address, it is someone
does DAD, otherwise we ignore solicitations
until DAD timer expires.
*/
if (addr_type == IPV6_ADDR_ANY) {
if (dev->type == ARPHRD_IEEE802_TR) {
unsigned char *sadr = skb->mac.raw ;
if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
(sadr[9] != dev->dev_addr[1]) ||
(sadr[10] != dev->dev_addr[2]) ||
(sadr[11] != dev->dev_addr[3]) ||
(sadr[12] != dev->dev_addr[4]) ||
(sadr[13] != dev->dev_addr[5]))
{
addrconf_dad_failure(ifp) ;
}
} else {
addrconf_dad_failure(ifp);
}
} else
in6_ifa_put(ifp);
return;
}
if (addr_type == IPV6_ADDR_ANY) {
struct in6_addr maddr;
ipv6_addr_all_nodes(&maddr);
ndisc_send_na(dev, NULL, &maddr, &ifp->addr,
ifp->idev->cnf.forwarding, 0,
ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1,
1);
in6_ifa_put(ifp);
return;
}
if (addr_type & IPV6_ADDR_UNICAST) {
int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
if (inc)
nd_tbl.stats.rcv_probes_mcast++;
else
nd_tbl.stats.rcv_probes_ucast++;
/*
* update / create cache entry
* for the source adddress
*/
neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
if (neigh || !dev->hard_header) {
ndisc_send_na(dev, neigh, saddr, &ifp->addr,
ifp->idev->cnf.forwarding, 1,
ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1,
1);
if (neigh)
neigh_release(neigh);
}
}
in6_ifa_put(ifp);
} else {
struct inet6_dev *in6_dev = in6_dev_get(dev);
int addr_type = ipv6_addr_type(saddr);
if (in6_dev && in6_dev->cnf.forwarding &&
(addr_type & IPV6_ADDR_UNICAST) &&
pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
if (skb->stamp.tv_sec == 0 ||
skb->pkt_type == PACKET_HOST ||
inc == 0 ||
in6_dev->nd_parms->proxy_delay == 0) {
if (inc)
nd_tbl.stats.rcv_probes_mcast++;
else
nd_tbl.stats.rcv_probes_ucast++;
neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
if (neigh) {
ndisc_send_na(dev, neigh, saddr, &msg->target,
0, 1, 0, 1);
neigh_release(neigh);
}
} else {
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
if (n)
pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
in6_dev_put(in6_dev);
return;
}
}
if (in6_dev)
in6_dev_put(in6_dev);
}
return;
}
void ndisc_recv_na(struct sk_buff *skb)
{
struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
u8 *lladdr = NULL;
int lladdrlen = 0;
u32 ndoptlen = skb->tail - msg->opt;
struct ndisc_options ndopts;
struct net_device *dev = skb->dev;
struct inet6_ifaddr *ifp;
struct neighbour *neigh;
if (skb->len < sizeof(struct nd_msg)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NA: packet too short\n");
return;
}
if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
if (net_ratelimit())
printk(KERN_WARNING "NDISC NA: target address is multicast\n");
return;
}
if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) &&
msg->icmph.icmp6_solicited) {
ND_PRINTK0("NDISC: solicited NA is multicasted\n");
return;
}
if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
return;
}
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
if (net_ratelimit())
printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
return;
}
}
if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
if (ifp->flags & IFA_F_TENTATIVE) {
addrconf_dad_failure(ifp);
return;
}
/* What should we make now? The advertisement
is invalid, but ndisc specs say nothing
about it. It could be misconfiguration, or
an smart proxy agent tries to help us :-)
*/
ND_PRINTK0("%s: someone advertises our address!\n",
ifp->idev->dev->name);
in6_ifa_put(ifp);
return;
}
neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
if (neigh) {
if (neigh->flags & NTF_ROUTER) {
if (msg->icmph.icmp6_router == 0) {
/*
* Change: router to host
*/
struct rt6_info *rt;
rt = rt6_get_dflt_router(saddr, dev);
if (rt)
ip6_del_rt(rt);
}
} else {
if (msg->icmph.icmp6_router)
neigh->flags |= NTF_ROUTER;
}
neigh_update(neigh, lladdr,
msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
msg->icmph.icmp6_override, 1);
neigh_release(neigh);
}
}
static void ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
......@@ -995,12 +1238,7 @@ static void pndisc_redo(struct sk_buff *skb)
int ndisc_rcv(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
struct neighbour *neigh;
struct inet6_ifaddr *ifp;
__skb_push(skb, skb->data-skb->h.raw);
......@@ -1020,240 +1258,12 @@ int ndisc_rcv(struct sk_buff *skb)
switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION:
{
struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
u8 *lladdr = NULL;
int lladdrlen = 0;
u32 ndoptlen = skb->tail - msg->opt;
struct ndisc_options ndopts;
if (skb->len < sizeof(struct nd_msg)) {
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 (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
return 0;
}
if (ndopts.nd_opts_src_lladdr) {
lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: bad lladdr length.\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);
if (ifp->flags & IFA_F_TENTATIVE) {
/* Address is tentative. If the source
is unspecified address, it is someone
does DAD, otherwise we ignore solicitations
until DAD timer expires.
*/
if (addr_type == IPV6_ADDR_ANY) {
if (dev->type == ARPHRD_IEEE802_TR) {
unsigned char *sadr = skb->mac.raw ;
if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
(sadr[9] != dev->dev_addr[1]) ||
(sadr[10] != dev->dev_addr[2]) ||
(sadr[11] != dev->dev_addr[3]) ||
(sadr[12] != dev->dev_addr[4]) ||
(sadr[13] != dev->dev_addr[5]))
{
addrconf_dad_failure(ifp) ;
}
} else {
addrconf_dad_failure(ifp);
}
} else
in6_ifa_put(ifp);
return 0;
}
if (addr_type == IPV6_ADDR_ANY) {
struct in6_addr maddr;
ipv6_addr_all_nodes(&maddr);
ndisc_send_na(dev, NULL, &maddr, &ifp->addr,
ifp->idev->cnf.forwarding, 0,
ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1,
1);
in6_ifa_put(ifp);
return 0;
}
if (addr_type & IPV6_ADDR_UNICAST) {
int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
if (inc)
nd_tbl.stats.rcv_probes_mcast++;
else
nd_tbl.stats.rcv_probes_ucast++;
/*
* update / create cache entry
* for the source adddress
*/
neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
if (neigh || !dev->hard_header) {
ndisc_send_na(dev, neigh, saddr, &ifp->addr,
ifp->idev->cnf.forwarding, 1,
ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1,
1);
if (neigh)
neigh_release(neigh);
}
}
in6_ifa_put(ifp);
} else {
struct inet6_dev *in6_dev = in6_dev_get(dev);
int addr_type = ipv6_addr_type(saddr);
if (in6_dev && in6_dev->cnf.forwarding &&
(addr_type & IPV6_ADDR_UNICAST) &&
pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
if (skb->stamp.tv_sec == 0 ||
skb->pkt_type == PACKET_HOST ||
inc == 0 ||
in6_dev->nd_parms->proxy_delay == 0) {
if (inc)
nd_tbl.stats.rcv_probes_mcast++;
else
nd_tbl.stats.rcv_probes_ucast++;
neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
if (neigh) {
ndisc_send_na(dev, neigh, saddr, &msg->target,
0, 1, 0, 1);
neigh_release(neigh);
}
} else {
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
if (n)
pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
in6_dev_put(in6_dev);
return 0;
}
}
if (in6_dev)
in6_dev_put(in6_dev);
}
return 0;
}
ndisc_recv_ns(skb);
break;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
{
struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
u8 *lladdr = NULL;
int lladdrlen = 0;
u32 ndoptlen = skb->tail - msg->opt;
struct ndisc_options ndopts;
if (skb->len < sizeof(struct nd_msg)) {
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 (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
if (net_ratelimit())
printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
return 0;
}
if (ndopts.nd_opts_tgt_lladdr) {
lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
if (net_ratelimit())
printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
return 0;
}
}
if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
if (ifp->flags & IFA_F_TENTATIVE) {
addrconf_dad_failure(ifp);
return 0;
}
/* What should we make now? The advertisement
is invalid, but ndisc specs say nothing
about it. It could be misconfiguration, or
an smart proxy agent tries to help us :-)
*/
ND_PRINTK0("%s: someone advertises our address!\n",
ifp->idev->dev->name);
in6_ifa_put(ifp);
return 0;
}
neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
if (neigh) {
if (neigh->flags & NTF_ROUTER) {
if (msg->icmph.icmp6_router == 0) {
/*
* Change: router to host
*/
struct rt6_info *rt;
rt = rt6_get_dflt_router(saddr, skb->dev);
if (rt)
ip6_del_rt(rt);
}
} else {
if (msg->icmph.icmp6_router)
neigh->flags |= NTF_ROUTER;
}
neigh_update(neigh, lladdr,
msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
msg->icmph.icmp6_override, 1);
neigh_release(neigh);
}
ndisc_recv_na(skb);
break;
}
case NDISC_ROUTER_ADVERTISEMENT:
ndisc_router_discovery(skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment