提交 9acefd17 编写于 作者: D David S. Miller

Merge branch 'ipv6_ndisc'

YOSHIFUJI Hideaki says:

====================
This series of changes basically clean up NDISC logic,
especially on sender side.

We originally do For NS/NA/RS:
 1) build temporary ICMPv6 header
 2) ndisc_build_skb() with temporary ICMPv6 header and rather
    criptic arguments.
    - Calculate total length and allocate sk_buff
    - Build IPv6 header.
    - copy ICMPv6 header, additional data and ND options.
    - Fill-in ICMPv6 checksum.
    Here, structures defined for message format was not used
    at all, it is difficult to understand what is being sent,
    and it was not generic.
 3) __ndisc_send()
    - Allocate temporary dst.
    - Send it.

Several issues:
- We could not defer decision if we should/can send some ND
  option.
- It is hard to see the packet format at a glance.
- ICMPv6 header was built as temporary variable, and then
  copied to the buffer.
- Some code path for Redirect was not shared.

With these patches, we do:
 1) Calculate (or estimate) message length and option length.
 2) Allocate skb (via new ndisc_skb_alloc()).
 3) Fill-in ICMPv6 message directly using compound literals.
 4) Fill-in ICMPv6 checksum
 5) Build IPv6 header (including length)
 6) Send the packet (via ndisc_send_skb()).
    - allocate temporary dst and send it.

- We can defer calculating real length of the packet.
  For example, we can give up filling some option at when
  filling in.
- Message is built directly without temporary buffer.
- Structures defined for message format is easier to understand
  what is being built.
- NS/NA/RS/Redirect share same logic.
- Reduced code/data size:
	   text	   data	    bss	    dec	    hex	filename
	 265407	  14133	   3488	 283028	  45194	old/net/ipv6/ipv6.o
	 264955	  14109	   3488	 282552	  44fb8	new/net/ipv6/ipv6.o
====================
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
...@@ -661,13 +661,6 @@ extern int ip6_xmit(struct sock *sk, ...@@ -661,13 +661,6 @@ extern int ip6_xmit(struct sock *sk,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
int tclass); int tclass);
extern int ip6_nd_hdr(struct sock *sk,
struct sk_buff *skb,
struct net_device *dev,
const struct in6_addr *saddr,
const struct in6_addr *daddr,
int proto, int len);
extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr);
extern int ip6_append_data(struct sock *sk, extern int ip6_append_data(struct sock *sk,
......
...@@ -127,13 +127,19 @@ static int ndisc_addr_option_pad(unsigned short type) ...@@ -127,13 +127,19 @@ static int ndisc_addr_option_pad(unsigned short type)
} }
} }
static inline int ndisc_opt_addr_space(struct net_device *dev)
{
return NDISC_OPT_SPACE(dev->addr_len +
ndisc_addr_option_pad(dev->type));
}
static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p, static inline u8 *ndisc_opt_addr_data(struct nd_opt_hdr *p,
struct net_device *dev) struct net_device *dev)
{ {
u8 *lladdr = (u8 *)(p + 1); u8 *lladdr = (u8 *)(p + 1);
int lladdrlen = p->nd_opt_len << 3; int lladdrlen = p->nd_opt_len << 3;
int prepad = ndisc_addr_option_pad(dev->type); int prepad = ndisc_addr_option_pad(dev->type);
if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len + prepad)) if (lladdrlen != ndisc_opt_addr_space(dev))
return NULL; return NULL;
return lladdr + prepad; return lladdr + prepad;
} }
......
...@@ -254,39 +254,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, ...@@ -254,39 +254,6 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
EXPORT_SYMBOL(ip6_xmit); EXPORT_SYMBOL(ip6_xmit);
/*
* To avoid extra problems ND packets are send through this
* routine. It's code duplication but I really want to avoid
* extra checks since ipv6_build_header is used by TCP (which
* is for us performance critical)
*/
int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct net_device *dev,
const struct in6_addr *saddr, const struct in6_addr *daddr,
int proto, int len)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct ipv6hdr *hdr;
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
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 = proto;
hdr->hop_limit = np->hop_limit;
hdr->saddr = *saddr;
hdr->daddr = *daddr;
return 0;
}
static int ip6_call_ra_chain(struct sk_buff *skb, int sel) static int ip6_call_ra_chain(struct sk_buff *skb, int sel)
{ {
struct ip6_ra_chain *ra; struct ip6_ra_chain *ra;
......
...@@ -1313,6 +1313,31 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted) ...@@ -1313,6 +1313,31 @@ mld_scount(struct ifmcaddr6 *pmc, int type, int gdeleted, int sdeleted)
return scount; return scount;
} }
static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb,
struct net_device *dev,
const struct in6_addr *saddr,
const struct in6_addr *daddr,
int proto, int len)
{
struct ipv6hdr *hdr;
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
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 = proto;
hdr->hop_limit = inet6_sk(sk)->hop_limit;
hdr->saddr = *saddr;
hdr->daddr = *daddr;
}
static struct sk_buff *mld_newpack(struct net_device *dev, int size) static struct sk_buff *mld_newpack(struct net_device *dev, int size)
{ {
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
...@@ -1348,7 +1373,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) ...@@ -1348,7 +1373,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
} else } else
saddr = &addr_buf; saddr = &addr_buf;
ip6_nd_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0); ip6_mc_hdr(sk, skb, dev, saddr, &mld2_all_mcr, NEXTHDR_HOP, 0);
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
...@@ -1740,7 +1765,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) ...@@ -1740,7 +1765,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
} else } else
saddr = &addr_buf; saddr = &addr_buf;
ip6_nd_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len); ip6_mc_hdr(sk, skb, dev, saddr, snd_addr, NEXTHDR_HOP, payload_len);
memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra)); memcpy(skb_put(skb, sizeof(ra)), ra, sizeof(ra));
......
...@@ -143,16 +143,12 @@ struct neigh_table nd_tbl = { ...@@ -143,16 +143,12 @@ struct neigh_table nd_tbl = {
.gc_thresh3 = 1024, .gc_thresh3 = 1024,
}; };
static inline int ndisc_opt_addr_space(struct net_device *dev) static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
{ {
return NDISC_OPT_SPACE(dev->addr_len + ndisc_addr_option_pad(dev->type)); int pad = ndisc_addr_option_pad(skb->dev->type);
} int data_len = skb->dev->addr_len;
int space = ndisc_opt_addr_space(skb->dev);
static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, u8 *opt = skb_put(skb, space);
unsigned short addr_type)
{
int pad = ndisc_addr_option_pad(addr_type);
int space = NDISC_OPT_SPACE(data_len + pad);
opt[0] = type; opt[0] = type;
opt[1] = space>>3; opt[1] = space>>3;
...@@ -166,7 +162,6 @@ static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len, ...@@ -166,7 +162,6 @@ static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
opt += data_len; opt += data_len;
if ((space -= data_len) > 0) if ((space -= data_len) > 0)
memset(opt, 0, space); memset(opt, 0, space);
return opt + space;
} }
static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, static struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur,
...@@ -370,90 +365,88 @@ static void pndisc_destructor(struct pneigh_entry *n) ...@@ -370,90 +365,88 @@ static void pndisc_destructor(struct pneigh_entry *n)
ipv6_dev_mc_dec(dev, &maddr); ipv6_dev_mc_dec(dev, &maddr);
} }
static struct sk_buff *ndisc_build_skb(struct net_device *dev, static struct sk_buff *ndisc_alloc_skb(struct net_device *dev,
const struct in6_addr *daddr, int len)
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h,
const struct in6_addr *target,
int llinfo)
{ {
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
struct sk_buff *skb;
struct icmp6hdr *hdr;
int hlen = LL_RESERVED_SPACE(dev); int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom; int tlen = dev->needed_tailroom;
int len; struct sock *sk = dev_net(dev)->ipv6.ndisc_sk;
struct sk_buff *skb;
int err; int err;
u8 *opt;
if (!dev->addr_len)
llinfo = 0;
len = sizeof(struct icmp6hdr) + (target ? sizeof(*target) : 0);
if (llinfo)
len += ndisc_opt_addr_space(dev);
skb = sock_alloc_send_skb(sk, skb = sock_alloc_send_skb(sk,
(sizeof(struct ipv6hdr) + hlen + sizeof(struct ipv6hdr) + len + tlen,
len + hlen + tlen),
1, &err); 1, &err);
if (!skb) { if (!skb) {
ND_PRINTK(0, err, "ND: %s failed to allocate an skb, err=%d\n", ND_PRINTK(0, err, "ndisc: %s failed to allocate an skb, err=%d\n",
__func__, err); __func__, err);
return NULL; return NULL;
} }
skb_reserve(skb, hlen); skb->protocol = htons(ETH_P_IPV6);
ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); skb->dev = dev;
skb->transport_header = skb->tail; skb_reserve(skb, hlen + sizeof(struct ipv6hdr));
skb_put(skb, len); skb_reset_transport_header(skb);
hdr = (struct icmp6hdr *)skb_transport_header(skb); return skb;
memcpy(hdr, icmp6h, sizeof(*hdr)); }
opt = skb_transport_header(skb) + sizeof(struct icmp6hdr); static void ip6_nd_hdr(struct sk_buff *skb,
if (target) { const struct in6_addr *saddr,
*(struct in6_addr *)opt = *target; const struct in6_addr *daddr,
opt += sizeof(*target); int hop_limit, int len)
} {
struct ipv6hdr *hdr;
if (llinfo) skb_push(skb, sizeof(*hdr));
ndisc_fill_addr_option(opt, llinfo, dev->dev_addr, skb_reset_network_header(skb);
dev->addr_len, dev->type); hdr = ipv6_hdr(skb);
hdr->icmp6_cksum = csum_ipv6_magic(saddr, daddr, len, ip6_flow_hdr(hdr, 0, 0);
IPPROTO_ICMPV6,
csum_partial(hdr,
len, 0));
return skb; hdr->payload_len = htons(len);
hdr->nexthdr = IPPROTO_ICMPV6;
hdr->hop_limit = hop_limit;
hdr->saddr = *saddr;
hdr->daddr = *daddr;
} }
static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, static void ndisc_send_skb(struct sk_buff *skb,
const struct in6_addr *daddr, const struct in6_addr *daddr,
const struct in6_addr *saddr, const struct in6_addr *saddr)
struct icmp6hdr *icmp6h)
{ {
struct flowi6 fl6; struct dst_entry *dst = skb_dst(skb);
struct dst_entry *dst; struct net *net = dev_net(skb->dev);
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk; struct sock *sk = net->ipv6.ndisc_sk;
struct inet6_dev *idev; struct inet6_dev *idev;
int err; int err;
struct icmp6hdr *icmp6h = icmp6_hdr(skb);
u8 type; u8 type;
type = icmp6h->icmp6_type; type = icmp6h->icmp6_type;
icmpv6_flow_init(sk, &fl6, type, saddr, daddr, dev->ifindex); if (!dst) {
dst = icmp6_dst_alloc(dev, &fl6); struct sock *sk = net->ipv6.ndisc_sk;
if (IS_ERR(dst)) { struct flowi6 fl6;
kfree_skb(skb);
return; 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;
}
skb_dst_set(skb, dst);
} }
skb_dst_set(skb, dst); icmp6h->icmp6_cksum = csum_ipv6_magic(saddr, daddr, skb->len,
IPPROTO_ICMPV6,
csum_partial(icmp6h,
skb->len, 0));
ip6_nd_hdr(skb, saddr, daddr, inet6_sk(sk)->hop_limit, skb->len);
rcu_read_lock(); rcu_read_lock();
idev = __in6_dev_get(dst->dev); idev = __in6_dev_get(dst->dev);
...@@ -469,35 +462,17 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev, ...@@ -469,35 +462,17 @@ static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
rcu_read_unlock(); rcu_read_unlock();
} }
/*
* Send a Neighbour Discover packet
*/
static void __ndisc_send(struct net_device *dev,
const struct in6_addr *daddr,
const struct in6_addr *saddr,
struct icmp6hdr *icmp6h, const struct in6_addr *target,
int llinfo)
{
struct sk_buff *skb;
skb = ndisc_build_skb(dev, daddr, saddr, icmp6h, target, llinfo);
if (!skb)
return;
ndisc_send_skb(skb, dev, daddr, saddr, icmp6h);
}
static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
const struct in6_addr *daddr, const struct in6_addr *daddr,
const struct in6_addr *solicited_addr, const struct in6_addr *solicited_addr,
bool router, bool solicited, bool override, bool inc_opt) bool router, bool solicited, bool override, bool inc_opt)
{ {
struct sk_buff *skb;
struct in6_addr tmpaddr; struct in6_addr tmpaddr;
struct inet6_ifaddr *ifp; struct inet6_ifaddr *ifp;
const struct in6_addr *src_addr; const struct in6_addr *src_addr;
struct icmp6hdr icmp6h = { struct nd_msg *msg;
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT, int optlen = 0;
};
/* for anycast or proxy, solicited_addr != src_addr */ /* for anycast or proxy, solicited_addr != src_addr */
ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1); ifp = ipv6_get_ifaddr(dev_net(dev), solicited_addr, dev, 1);
...@@ -515,12 +490,32 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, ...@@ -515,12 +490,32 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh,
src_addr = &tmpaddr; src_addr = &tmpaddr;
} }
icmp6h.icmp6_router = router; if (!dev->addr_len)
icmp6h.icmp6_solicited = solicited; inc_opt = 0;
icmp6h.icmp6_override = override; if (inc_opt)
optlen += ndisc_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
return;
msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
*msg = (struct nd_msg) {
.icmph = {
.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT,
.icmp6_router = router,
.icmp6_solicited = solicited,
.icmp6_override = override,
},
.target = *solicited_addr,
};
__ndisc_send(dev, daddr, src_addr, &icmp6h, solicited_addr, if (inc_opt)
inc_opt ? ND_OPT_TARGET_LL_ADDR : 0); ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR,
dev->dev_addr);
ndisc_send_skb(skb, daddr, src_addr);
} }
static void ndisc_send_unsol_na(struct net_device *dev) static void ndisc_send_unsol_na(struct net_device *dev)
...@@ -548,10 +543,11 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -548,10 +543,11 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
const struct in6_addr *solicit, const struct in6_addr *solicit,
const struct in6_addr *daddr, const struct in6_addr *saddr) const struct in6_addr *daddr, const struct in6_addr *saddr)
{ {
struct sk_buff *skb;
struct in6_addr addr_buf; struct in6_addr addr_buf;
struct icmp6hdr icmp6h = { int inc_opt = dev->addr_len;
.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION, int optlen = 0;
}; struct nd_msg *msg;
if (saddr == NULL) { if (saddr == NULL) {
if (ipv6_get_lladdr(dev, &addr_buf, if (ipv6_get_lladdr(dev, &addr_buf,
...@@ -560,17 +556,37 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, ...@@ -560,17 +556,37 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh,
saddr = &addr_buf; saddr = &addr_buf;
} }
__ndisc_send(dev, daddr, saddr, &icmp6h, solicit, if (ipv6_addr_any(saddr))
!ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0); inc_opt = 0;
if (inc_opt)
optlen += ndisc_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
return;
msg = (struct nd_msg *)skb_put(skb, sizeof(*msg));
*msg = (struct nd_msg) {
.icmph = {
.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION,
},
.target = *solicit,
};
if (inc_opt)
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
dev->dev_addr);
ndisc_send_skb(skb, daddr, saddr);
} }
void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
const struct in6_addr *daddr) const struct in6_addr *daddr)
{ {
struct icmp6hdr icmp6h = { struct sk_buff *skb;
.icmp6_type = NDISC_ROUTER_SOLICITATION, struct rs_msg *msg;
};
int send_sllao = dev->addr_len; int send_sllao = dev->addr_len;
int optlen = 0;
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD #ifdef CONFIG_IPV6_OPTIMISTIC_DAD
/* /*
...@@ -594,8 +610,27 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr, ...@@ -594,8 +610,27 @@ void ndisc_send_rs(struct net_device *dev, const struct in6_addr *saddr,
} }
} }
#endif #endif
__ndisc_send(dev, daddr, saddr, &icmp6h, NULL, if (!dev->addr_len)
send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0); send_sllao = 0;
if (send_sllao)
optlen += ndisc_opt_addr_space(dev);
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
if (!skb)
return;
msg = (struct rs_msg *)skb_put(skb, sizeof(*msg));
*msg = (struct rs_msg) {
.icmph = {
.icmp6_type = NDISC_ROUTER_SOLICITATION,
},
};
if (send_sllao)
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
dev->dev_addr);
ndisc_send_skb(skb, daddr, saddr);
} }
...@@ -1346,24 +1381,34 @@ static void ndisc_redirect_rcv(struct sk_buff *skb) ...@@ -1346,24 +1381,34 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
icmpv6_notify(skb, NDISC_REDIRECT, 0, 0); icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
} }
static void ndisc_fill_redirect_hdr_option(struct sk_buff *skb,
struct sk_buff *orig_skb,
int rd_len)
{
u8 *opt = skb_put(skb, 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);
}
void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
{ {
struct net_device *dev = skb->dev; struct net_device *dev = skb->dev;
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk; struct sock *sk = net->ipv6.ndisc_sk;
int len = sizeof(struct rd_msg); int optlen = 0;
struct inet_peer *peer; struct inet_peer *peer;
struct sk_buff *buff; struct sk_buff *buff;
struct rd_msg *msg; struct rd_msg *msg;
struct in6_addr saddr_buf; struct in6_addr saddr_buf;
struct rt6_info *rt; struct rt6_info *rt;
struct dst_entry *dst; struct dst_entry *dst;
struct inet6_dev *idev;
struct flowi6 fl6; struct flowi6 fl6;
u8 *opt;
int hlen, tlen;
int rd_len; int rd_len;
int err;
u8 ha_buf[MAX_ADDR_LEN], *ha = NULL; u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
bool ret; bool ret;
...@@ -1419,7 +1464,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) ...@@ -1419,7 +1464,7 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
memcpy(ha_buf, neigh->ha, dev->addr_len); memcpy(ha_buf, neigh->ha, dev->addr_len);
read_unlock_bh(&neigh->lock); read_unlock_bh(&neigh->lock);
ha = ha_buf; ha = ha_buf;
len += ndisc_opt_addr_space(dev); optlen += ndisc_opt_addr_space(dev);
} else } else
read_unlock_bh(&neigh->lock); read_unlock_bh(&neigh->lock);
...@@ -1427,78 +1472,40 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target) ...@@ -1427,78 +1472,40 @@ void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target)
} }
rd_len = min_t(unsigned int, rd_len = min_t(unsigned int,
IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8); IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(*msg) - optlen,
skb->len + 8);
rd_len &= ~0x7; rd_len &= ~0x7;
len += rd_len; optlen += rd_len;
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
buff = sock_alloc_send_skb(sk,
(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);
goto release;
}
skb_reserve(buff, hlen);
ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,
IPPROTO_ICMPV6, len);
skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data); buff = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
skb_put(buff, len); if (!buff)
msg = (struct rd_msg *)icmp6_hdr(buff); goto release;
memset(&msg->icmph, 0, sizeof(struct icmp6hdr));
msg->icmph.icmp6_type = NDISC_REDIRECT;
/*
* copy target and destination addresses
*/
msg->target = *target;
msg->dest = ipv6_hdr(skb)->daddr;
opt = msg->opt; msg = (struct rd_msg *)skb_put(buff, sizeof(*msg));
*msg = (struct rd_msg) {
.icmph = {
.icmp6_type = NDISC_REDIRECT,
},
.target = *target,
.dest = ipv6_hdr(skb)->daddr,
};
/* /*
* include target_address option * include target_address option
*/ */
if (ha) if (ha)
opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha, ndisc_fill_addr_option(skb, ND_OPT_TARGET_LL_ADDR, ha);
dev->addr_len, dev->type);
/* /*
* build redirect option and copy skb over to the new packet. * build redirect option and copy skb over to the new packet.
*/ */
memset(opt, 0, 8); if (rd_len)
*(opt++) = ND_OPT_REDIRECT_HDR; ndisc_fill_redirect_hdr_option(buff, skb, rd_len);
*(opt++) = (rd_len >> 3);
opt += 6;
memcpy(opt, ipv6_hdr(skb), rd_len - 8);
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); skb_dst_set(buff, dst);
rcu_read_lock(); ndisc_send_skb(buff, &ipv6_hdr(skb)->saddr, &saddr_buf);
idev = __in6_dev_get(dst->dev);
IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_OUT, skb->len);
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, buff, NULL, dst->dev,
dst_output);
if (!err) {
ICMP6MSGOUT_INC_STATS(net, idev, NDISC_REDIRECT);
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS);
}
rcu_read_unlock();
return; return;
release: release:
...@@ -1515,7 +1522,7 @@ int ndisc_rcv(struct sk_buff *skb) ...@@ -1515,7 +1522,7 @@ int ndisc_rcv(struct sk_buff *skb)
{ {
struct nd_msg *msg; struct nd_msg *msg;
if (!pskb_may_pull(skb, skb->len)) if (skb_linearize(skb))
return 0; return 0;
msg = (struct nd_msg *)skb_transport_header(skb); msg = (struct nd_msg *)skb_transport_header(skb);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册