Commit e1ec7842 authored by YOSHIFUJI Hideaki's avatar YOSHIFUJI Hideaki Committed by David S. Miller

[IPV6] NDISC: Unify main process of sending ND messages.

Because ndisc_send_na(), ndisc_send_ns() and ndisc_send_rs()
are almost identical, so let's unify their common part.

With gcc (GCC) 3.3.5 (Debian 1:3.3.5-13) on i386,
	Before:
	   text	   data	    bss	    dec	    hex	filename
	  14689	    364	     24	  15077	   3ae5	net/ipv6/ndisc.o
	After:
	   text	   data	    bss	    dec	    hex	filename
	  12317	    364	     24	  12705	   31a1	net/ipv6/ndisc.o
Signed-off-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
parent c53b3590
...@@ -427,38 +427,23 @@ static inline void ndisc_flow_init(struct flowi *fl, u8 type, ...@@ -427,38 +427,23 @@ static inline void ndisc_flow_init(struct flowi *fl, u8 type,
security_sk_classify_flow(ndisc_socket->sk, fl); security_sk_classify_flow(ndisc_socket->sk, fl);
} }
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, static void __ndisc_send(struct net_device *dev,
struct in6_addr *daddr, struct in6_addr *solicited_addr, struct neighbour *neigh,
int router, int solicited, int override, int inc_opt) struct in6_addr *daddr, struct in6_addr *saddr,
struct icmp6hdr *icmp6h, struct in6_addr *target,
int llinfo, int icmp6_mib_outnd)
{ {
struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
struct flowi fl; struct flowi fl;
struct dst_entry* dst; struct dst_entry *dst;
struct sock *sk = ndisc_socket->sk; struct sock *sk = ndisc_socket->sk;
struct in6_addr *src_addr;
struct nd_msg *msg;
int len;
struct sk_buff *skb; struct sk_buff *skb;
struct icmp6hdr *hdr;
struct inet6_dev *idev;
int len;
int err; int err;
u8 *opt;
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); ndisc_flow_init(&fl, icmp6h->icmp6_type, saddr, daddr,
/* for anycast or proxy, solicited_addr != src_addr */
ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
if (ifp) {
src_addr = solicited_addr;
if (ifp->flags & IFA_F_OPTIMISTIC)
override = 0;
in6_ifa_put(ifp);
} else {
if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
return;
src_addr = &tmpaddr;
}
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr,
dev->ifindex); dev->ifindex);
dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output);
...@@ -469,61 +454,57 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -469,61 +454,57 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
if (err < 0) if (err < 0)
return; return;
if (inc_opt) { if (!dev->addr_len)
if (dev->addr_len) llinfo = 0;
len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
if (llinfo)
len += ndisc_opt_addr_space(dev); len += ndisc_opt_addr_space(dev);
else
inc_opt = 0;
}
skb = sock_alloc_send_skb(sk, skb = sock_alloc_send_skb(sk,
(MAX_HEADER + sizeof(struct ipv6hdr) + (MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_RESERVED_SPACE(dev)), len + LL_RESERVED_SPACE(dev)),
1, &err); 1, &err);
if (!skb) {
if (skb == NULL) {
ND_PRINTK0(KERN_ERR ND_PRINTK0(KERN_ERR
"ICMPv6 NA: %s() failed to allocate an skb.\n", "ICMPv6 ND: %s() failed to allocate an skb.\n",
__FUNCTION__); __FUNCTION__);
dst_release(dst); dst_release(dst);
return; return;
} }
skb_reserve(skb, LL_RESERVED_SPACE(dev)); skb_reserve(skb, LL_RESERVED_SPACE(dev));
ip6_nd_hdr(sk, skb, dev, src_addr, daddr, IPPROTO_ICMPV6, len); ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
skb->transport_header = skb->tail; skb->transport_header = skb->tail;
skb_put(skb, len); skb_put(skb, len);
msg = (struct nd_msg *)skb_transport_header(skb);
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; hdr = (struct icmp6hdr *)skb_transport_header(skb);
msg->icmph.icmp6_code = 0; memcpy(hdr, icmp6h, sizeof(*hdr));
msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0; opt = skb_transport_header(skb) + sizeof(struct icmp6hdr);
msg->icmph.icmp6_router = router; if (target) {
msg->icmph.icmp6_solicited = solicited; ipv6_addr_copy((struct in6_addr *)opt, target);
msg->icmph.icmp6_override = override; opt += sizeof(*target);
}
/* Set the target address. */
ipv6_addr_copy(&msg->target, solicited_addr);
if (inc_opt) if (llinfo)
ndisc_fill_addr_option(msg->opt, ND_OPT_TARGET_LL_ADDR, dev->dev_addr, ndisc_fill_addr_option(opt, llinfo, dev->dev_addr,
dev->addr_len, dev->type); dev->addr_len, dev->type);
/* checksum */ hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len,
msg->icmph.icmp6_cksum = csum_ipv6_magic(src_addr, daddr, len,
IPPROTO_ICMPV6, IPPROTO_ICMPV6,
csum_partial((__u8 *) msg, csum_partial((__u8 *) hdr,
len, 0)); len, 0));
skb->dst = dst; skb->dst = dst;
idev = in6_dev_get(dst->dev); idev = in6_dev_get(dst->dev);
IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS); IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
if (!err) { if (!err) {
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS); ICMP6_INC_STATS(idev, icmp6_mib_outnd);
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
} }
...@@ -531,20 +512,48 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -531,20 +512,48 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
in6_dev_put(idev); in6_dev_put(idev);
} }
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
struct in6_addr *daddr, struct in6_addr *solicited_addr,
int router, int solicited, int override, int inc_opt)
{
struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp;
struct in6_addr *src_addr;
struct icmp6hdr icmp6h = {
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
};
/* for anycast or proxy, solicited_addr != src_addr */
ifp = ipv6_get_ifaddr(solicited_addr, dev, 1);
if (ifp) {
src_addr = solicited_addr;
if (ifp->flags & IFA_F_OPTIMISTIC)
override = 0;
in6_ifa_put(ifp);
} else {
if (ipv6_dev_get_saddr(dev, daddr, &tmpaddr))
return;
src_addr = &tmpaddr;
}
icmp6h.icmp6_router = router;
icmp6h.icmp6_solicited = solicited;
icmp6h.icmp6_override = override;
__ndisc_send(dev, neigh, daddr, src_addr,
&icmp6h, solicited_addr,
inc_opt ? ND_OPT_TARGET_LL_ADDR : 0,
ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);
}
void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
struct in6_addr *solicit, struct in6_addr *solicit,
struct in6_addr *daddr, struct in6_addr *saddr) struct in6_addr *daddr, struct in6_addr *saddr)
{ {
struct flowi fl;
struct dst_entry* dst;
struct inet6_dev *idev;
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
struct in6_addr addr_buf; struct in6_addr addr_buf;
int len; struct icmp6hdr icmp6h = {
int err; .icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
int send_llinfo; };
if (saddr == NULL) { if (saddr == NULL) {
if (ipv6_get_lladdr(dev, &addr_buf, if (ipv6_get_lladdr(dev, &addr_buf,
...@@ -553,86 +562,19 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -553,86 +562,19 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
saddr = &addr_buf; saddr = &addr_buf;
} }
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr, __ndisc_send(dev, neigh, daddr, saddr,
dev->ifindex); &icmp6h, solicit,
!ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0,
dst = ndisc_dst_alloc(dev, neigh, daddr, ip6_output); ICMP6_MIB_OUTNEIGHBORSOLICITS);
if (!dst)
return;
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0)
return;
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
send_llinfo = dev->addr_len && !ipv6_addr_any(saddr);
if (send_llinfo)
len += ndisc_opt_addr_space(dev);
skb = sock_alloc_send_skb(sk,
(MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_RESERVED_SPACE(dev)),
1, &err);
if (skb == NULL) {
ND_PRINTK0(KERN_ERR
"ICMPv6 NA: %s() failed to allocate an skb.\n",
__FUNCTION__);
dst_release(dst);
return;
}
skb_reserve(skb, LL_RESERVED_SPACE(dev));
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
skb->transport_header = skb->tail;
skb_put(skb, len);
msg = (struct nd_msg *)skb_transport_header(skb);
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
msg->icmph.icmp6_code = 0;
msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0;
/* Set the target address. */
ipv6_addr_copy(&msg->target, solicit);
if (send_llinfo)
ndisc_fill_addr_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
dev->addr_len, dev->type);
/* checksum */
msg->icmph.icmp6_cksum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
daddr, len,
IPPROTO_ICMPV6,
csum_partial((__u8 *) msg,
len, 0));
/* send it! */
skb->dst = dst;
idev = in6_dev_get(dst->dev);
IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
if (!err) {
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS);
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
}
if (likely(idev != NULL))
in6_dev_put(idev);
} }
void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
struct in6_addr *daddr) struct in6_addr *daddr)
{ {
struct flowi fl; struct icmp6hdr icmp6h = {
struct dst_entry* dst; .icmp6_type = NDISC_ROUTER_SOLICITATION,
struct inet6_dev *idev; };
struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
__u8 * opt;
int send_sllao = dev->addr_len; int send_sllao = dev->addr_len;
int len;
int err;
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
/* /*
...@@ -655,67 +597,10 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, ...@@ -655,67 +597,10 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,
} }
} }
#endif #endif
ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr, __ndisc_send(dev, NULL, daddr, saddr,
dev->ifindex); &icmp6h, NULL,
send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0,
dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); ICMP6_MIB_OUTROUTERSOLICITS);
if (!dst)
return;
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0)
return;
len = sizeof(struct icmp6hdr);
if (send_sllao)
len += ndisc_opt_addr_space(dev);
skb = sock_alloc_send_skb(sk,
(MAX_HEADER + sizeof(struct ipv6hdr) +
len + LL_RESERVED_SPACE(dev)),
1, &err);
if (skb == NULL) {
ND_PRINTK0(KERN_ERR
"ICMPv6 RS: %s() failed to allocate an skb.\n",
__FUNCTION__);
dst_release(dst);
return;
}
skb_reserve(skb, LL_RESERVED_SPACE(dev));
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
skb->transport_header = skb->tail;
skb_put(skb, len);
hdr = icmp6_hdr(skb);
hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
hdr->icmp6_code = 0;
hdr->icmp6_cksum = 0;
hdr->icmp6_unused = 0;
opt = (u8*) (hdr + 1);
if (send_sllao)
ndisc_fill_addr_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr,
dev->addr_len, dev->type);
/* checksum */
hdr->icmp6_cksum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr, daddr, len,
IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0));
/* send it! */
skb->dst = dst;
idev = in6_dev_get(dst->dev);
IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
if (!err) {
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS);
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
}
if (likely(idev != NULL))
in6_dev_put(idev);
} }
......
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