-static inline int ip6_rcv_finish( struct sk_buff *skb)
+inline int ip6_rcv_finish( struct sk_buff *skb)
{
if (skb->dst == NULL)
ip6_route_input(skb);
return dst_input(skb);
}
-int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
+int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
struct ipv6hdr *hdr;
u32 pkt_len;
goto out;
}
- /* Store incoming device index. When the packet will
- be queued, we cannot refer to skb->dev anymore.
+ /*
+ * Store incoming device index. When the packet will
+ * be queued, we cannot refer to skb->dev anymore.
+ *
+ * BTW, when we send a packet for our own local address on a
+ * non-loopback interface (e.g. ethX), it is being delivered
+ * via the loopback interface (lo) here; skb->dev = &loopback_dev.
+ * It, however, should be considered as if it is being
+ * arrived via the sending interface (ethX), because of the
+ * nature of scoping architecture. --yoshfuji
*/
- IP6CB(skb)->iif = dev->ifindex;
+ IP6CB(skb)->iif = skb->dst ? ((struct rt6_info *)skb->dst)->rt6i_idev->dev->ifindex : dev->ifindex;
if (skb->len < sizeof(struct ipv6hdr))
goto err;
if (hdr->version != 6)
goto err;
+ skb->h.raw = (u8 *)(hdr + 1);
+ IP6CB(skb)->nhoff = offsetof(struct ipv6hdr, nexthdr);
+
pkt_len = ntohs(hdr->payload_len);
/* pkt_len may be zero if Jumbo payload option is present */
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
goto truncated;
- if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
- if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))){
- IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
- goto drop;
- }
- hdr = skb->nh.ipv6h;
- if (skb->ip_summed == CHECKSUM_HW)
- skb->ip_summed = CHECKSUM_NONE;
+ if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) {
+ IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
+ goto drop;
}
+ hdr = skb->nh.ipv6h;
}
if (hdr->nexthdr == NEXTHDR_HOP) {
- skb->h.raw = (u8*)(hdr+1);
- if (ipv6_parse_hopopts(skb, offsetof(struct ipv6hdr, nexthdr)) < 0) {
+ if (ipv6_parse_hopopts(skb, IP6CB(skb)->nhoff) < 0) {
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
return 0;
}
unsigned int nhoff;
int nexthdr;
u8 hash;
- int cksum_sub = 0;
-
- skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
/*
* Parse extension headers
*/
- nexthdr = skb->nh.ipv6h->nexthdr;
- nhoff = offsetof(struct ipv6hdr, nexthdr);
-
- /* Skip hop-by-hop options, they are already parsed. */
- if (nexthdr == NEXTHDR_HOP) {
- nhoff = sizeof(struct ipv6hdr);
- nexthdr = skb->h.raw[0];
- skb->h.raw += (skb->h.raw[1]+1)<<3;
- }
-
rcu_read_lock();
resubmit:
if (!pskb_pull(skb, skb->h.raw - skb->data))
goto discard;
+ nhoff = IP6CB(skb)->nhoff;
nexthdr = skb->nh.raw[nhoff];
raw_sk = sk_head(&raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)]);
- if (raw_sk)
- ipv6_raw_deliver(skb, nexthdr);
+ if (raw_sk && !ipv6_raw_deliver(skb, nexthdr))
+ raw_sk = NULL;
hash = nexthdr & (MAX_INET_PROTOS - 1);
- if ((ipprot = inet6_protos[hash]) != NULL) {
+ if ((ipprot = rcu_dereference(inet6_protos[hash])) != NULL) {
int ret;
- smp_read_barrier_depends();
if (ipprot->flags & INET6_PROTO_FINAL) {
struct ipv6hdr *hdr;
- if (!cksum_sub && skb->ip_summed == CHECKSUM_HW) {
- skb->csum = csum_sub(skb->csum,
- csum_partial(skb->nh.raw, skb->h.raw-skb->nh.raw, 0));
- cksum_sub++;
- }
+ /* Free reference early: we don't need it any more,
+ and it may hold ip_conntrack module loaded
+ indefinitely. */
+ nf_reset(skb);
+
+ skb_postpull_rcsum(skb, skb->nh.raw,
+ skb->h.raw - skb->nh.raw);
hdr = skb->nh.ipv6h;
if (ipv6_addr_is_multicast(&hdr->daddr) &&
!ipv6_chk_mcast_addr(skb->dev, &hdr->daddr,
!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
goto discard;
- ret = ipprot->handler(&skb, &nhoff);
+ ret = ipprot->handler(&skb);
if (ret > 0)
goto resubmit;
else if (ret == 0)
if (!raw_sk) {
if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
IP6_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
- icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
+ icmpv6_send(skb, ICMPV6_PARAMPROB,
+ ICMPV6_UNK_NEXTHDR, nhoff,
+ skb->dev);
}
- } else {
+ } else
IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
- kfree_skb(skb);
- }
+ kfree_skb(skb);
}
rcu_read_unlock();
return 0;