exthdrs.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 653 行 · 第 1/2 页
C
653 行
/* * Extension Header handling for IPv6 * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * Andi Kleen <ak@muc.de> * Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> * * $Id: exthdrs.c,v 1.13 2001/06/19 15:58:56 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *//* Changes: * yoshfuji : ensure not to overrun while parsing * tlv options. * Mitsuru KANDA @USAGI and: Remove ipv6_parse_exthdrs(). * YOSHIFUJI Hideaki @USAGI Register inbound extension header * handlers as inet6_protocol{}. */#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/sched.h>#include <linux/net.h>#include <linux/netdevice.h>#include <linux/in6.h>#include <linux/icmpv6.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/protocol.h>#include <net/transp_v6.h>#include <net/rawv6.h>#include <net/ndisc.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <asm/uaccess.h>/* * Parsing tlv encoded headers. * * Parsing function "func" returns 1, if parsing succeed * and 0, if it failed. * It MUST NOT touch skb->h. */struct tlvtype_proc { int type; int (*func)(struct sk_buff *skb, int offset);};/********************* Generic functions *********************//* An unknown option is detected, decide what to do */static int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff){ switch ((skb->nh.raw[optoff] & 0xC0) >> 6) { case 0: /* ignore */ return 1; case 1: /* drop packet */ break; case 3: /* Send ICMP if not a multicast address and drop packet */ /* Actually, it is redundant check. icmp_send will recheck in any case. */ if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) break; case 2: /* send ICMP PARM PROB regardless and drop packet */ icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); return 0; }; kfree_skb(skb); return 0;}/* Parse tlv encoded option header (hop-by-hop or destination) */static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb){ struct tlvtype_proc *curr; int off = skb->h.raw - skb->nh.raw; int len = ((skb->h.raw[1]+1)<<3); if ((skb->h.raw + len) - skb->data > skb_headlen(skb)) goto bad; off += 2; len -= 2; while (len > 0) { int optlen = skb->nh.raw[off+1]+2; switch (skb->nh.raw[off]) { case IPV6_TLV_PAD0: optlen = 1; break; case IPV6_TLV_PADN: break; default: /* Other TLV code so scan list */ if (optlen > len) goto bad; for (curr=procs; curr->type >= 0; curr++) { if (curr->type == skb->nh.raw[off]) { /* type specific length/alignment checks will be performed in the func(). */ if (curr->func(skb, off) == 0) return 0; break; } } if (curr->type < 0) { if (ip6_tlvopt_unknown(skb, off) == 0) return 0; } break; } off += optlen; len -= optlen; } if (len == 0) return 1;bad: kfree_skb(skb); return 0;}/***************************** Destination options header. *****************************/static struct tlvtype_proc tlvprocdestopt_lst[] = { /* No destination options are defined now */ {-1, NULL}};static int ipv6_destopt_rcv(struct sk_buff **skbp, unsigned int *nhoffp){ struct sk_buff *skb = *skbp; struct inet6_skb_parm *opt = IP6CB(skb); if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); return -1; } opt->dst1 = skb->h.raw - skb->nh.raw; if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { skb->h.raw += ((skb->h.raw[1]+1)<<3); *nhoffp = opt->dst1; return 1; } IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); return -1;}static struct inet6_protocol destopt_protocol = { .handler = ipv6_destopt_rcv, .flags = INET6_PROTO_NOPOLICY,};void __init ipv6_destopt_init(void){ if (inet6_add_protocol(&destopt_protocol, IPPROTO_DSTOPTS) < 0) printk(KERN_ERR "ipv6_destopt_init: Could not register protocol\n");}/******************************** NONE header. No data in packet. ********************************/static int ipv6_nodata_rcv(struct sk_buff **skbp, unsigned int *nhoffp){ struct sk_buff *skb = *skbp; kfree_skb(skb); return 0;}static struct inet6_protocol nodata_protocol = { .handler = ipv6_nodata_rcv, .flags = INET6_PROTO_NOPOLICY,};void __init ipv6_nodata_init(void){ if (inet6_add_protocol(&nodata_protocol, IPPROTO_NONE) < 0) printk(KERN_ERR "ipv6_nodata_init: Could not register protocol\n");}/******************************** Routing header. ********************************/static int ipv6_rthdr_rcv(struct sk_buff **skbp, unsigned int *nhoffp){ struct sk_buff *skb = *skbp; struct inet6_skb_parm *opt = IP6CB(skb); struct in6_addr *addr; struct in6_addr daddr; int n, i; struct ipv6_rt_hdr *hdr; struct rt0_hdr *rthdr; if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); kfree_skb(skb); return -1; } hdr = (struct ipv6_rt_hdr *) skb->h.raw; if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) || skb->pkt_type != PACKET_HOST) { IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); kfree_skb(skb); return -1; }looped_back: if (hdr->segments_left == 0) { opt->srcrt = skb->h.raw - skb->nh.raw; skb->h.raw += (hdr->hdrlen + 1) << 3; opt->dst0 = opt->dst1; opt->dst1 = 0; *nhoffp = (&hdr->nexthdr) - skb->nh.raw; return 1; } if (hdr->type != IPV6_SRCRT_TYPE_0) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); return -1; } if (hdr->hdrlen & 0x01) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw); return -1; } /* * This is the routing header forwarding algorithm from * RFC 2460, page 16. */ n = hdr->hdrlen >> 1; if (hdr->segments_left > n) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw); return -1; } /* We are about to mangle packet header. Be careful! Do not damage packets queued somewhere. */ if (skb_cloned(skb)) { struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); kfree_skb(skb); /* the copy is a forwarded packet */ if (skb2 == NULL) { IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS); return -1; } *skbp = skb = skb2; opt = IP6CB(skb2); hdr = (struct ipv6_rt_hdr *) skb2->h.raw; } if (skb->ip_summed == CHECKSUM_HW) skb->ip_summed = CHECKSUM_NONE; i = n - --hdr->segments_left; rthdr = (struct rt0_hdr *) hdr; addr = rthdr->addr; addr += i - 1; if (ipv6_addr_is_multicast(addr)) { IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS); kfree_skb(skb); return -1; } ipv6_addr_copy(&daddr, addr); ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr); ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr); dst_release(xchg(&skb->dst, NULL)); ip6_route_input(skb); if (skb->dst->error) { skb_push(skb, skb->data - skb->nh.raw); dst_input(skb); return -1; } if (skb->dst->dev->flags&IFF_LOOPBACK) { if (skb->nh.ipv6h->hop_limit <= 1) { IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS); icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0, skb->dev); kfree_skb(skb);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?