patch-2.1.8 linux/net/ipv6/ipv6_input.c
Next file: linux/net/ipv6/ipv6_output.c
Previous file: linux/net/ipv6/icmp.c
Back to the patch index
Back to the overall index
- Lines: 438
- Date:
Sun Nov 3 11:04:46 1996
- Orig file:
v2.1.7/linux/net/ipv6/ipv6_input.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v2.1.7/linux/net/ipv6/ipv6_input.c linux/net/ipv6/ipv6_input.c
@@ -0,0 +1,437 @@
+/*
+ * IPv6 input
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Ian P. Morris <I.P.Morris@soton.ac.uk>
+ *
+ * Based in linux/net/ipv4/ip_input.c
+ *
+ * $Id: ipv6_input.c,v 1.13 1996/10/11 16:03:06 roque 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.
+ */
+
+
+#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/ipv6_route.h>
+#include <net/addrconf.h>
+
+/*
+ * Header processing function list
+ * We process headers in order (as per RFC)
+ * If the processing function returns 0 the packet is considered
+ * delivered else it returns the value of the nexthdr.
+ * The ptr field of the function points to the previous nexthdr field.
+ * This is allows the processing function to change it if it's sematics
+ * is: return a new packet without this header (like fragmentation).
+ * When a next_header value is not within the list
+ * the inet protocol list is searched (i.e. to deliver to
+ * TCP for instance)
+ */
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
+ struct ipv6_options *opt);
+
+
+struct hdrtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+} hdrproc_lst[] = {
+ /*
+ TODO
+
+ {NEXTHDR_HOP, ipv6_hop_by_hop}
+ */
+ {NEXTHDR_ROUTING, ipv6_routing_header},
+ {NEXTHDR_FRAGMENT, ipv6_reassembly},
+
+ {NEXTHDR_DEST, ipv6_dest_opt},
+ /*
+ {NEXTHDR_AUTH, ipv6_auth_hdr},
+ {NEXTHDR_ESP, ipv6_esp_hdr},
+ */
+ {NEXTHDR_MAX, NULL}
+};
+
+/* New header structures */
+
+
+struct ipv6_tlvtype {
+ u8 type;
+ u8 len;
+};
+
+struct ipv6_destopt_hdr {
+ u8 nexthdr;
+ u8 hdrlen;
+};
+
+
+struct tlvtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+
+ /* these functions do NOT update skb->h.raw */
+
+} tlvprocdestopt_lst[] = {
+ {255, NULL}
+};
+
+
+static int parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+ struct device *dev, __u8 *nhptr, struct ipv6_options *opt,
+ void *lastopt)
+{
+ struct ipv6_tlvtype *hdr;
+ struct tlvtype_proc *curr;
+ int pos;
+
+ while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt)
+ switch (hdr->type & 0x3F)
+ {
+ case 0: /* TLV encoded Pad1 */
+ skb->h.raw++;
+ break;
+
+ case 1: /* TLV encoded PadN */
+ skb->h.raw += hdr->len+2;
+ break;
+
+ default: /* Other TLV code so scan list */
+ for (curr=procs; curr->type != 255; curr++)
+ if (curr->type == (hdr->type & 0x3F))
+ {
+ curr->func(skb, dev, nhptr, opt);
+ skb->h.raw += hdr->len+2;
+ break;
+ }
+
+ if (curr->type==255)
+ {
+ /* unkown type */
+ pos= (__u8 *) skb->h.raw - (__u8 *) skb->ipv6_hdr;
+ /* I think this is correct please check - IPM */
+
+ switch ((hdr->type & 0xC0) >> 6) {
+ case 0: /* ignore */
+ skb->h.raw += hdr->len+2;
+ break;
+
+ case 1: /* drop packet */
+ kfree_skb(skb, FREE_READ);
+ return 0;
+
+ case 2: /* send ICMP PARM PROB regardless and
+ drop packet */
+ icmpv6_send(skb, ICMPV6_PARAMETER_PROB,
+ 2, pos, dev);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+
+ case 3: /* Send ICMP if not a multicast address
+ and drop packet */
+ if (!(ipv6_addr_type(&(skb->ipv6_hdr->daddr)) & IPV6_ADDR_MULTICAST) )
+ icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 2, pos, dev);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
+ struct ipv6_options *opt)
+{
+ struct sk_buff *skb=*skb_ptr;
+ struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+
+ if (parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,skb->h.raw+hdr->hdrlen))
+ return hdr->nexthdr;
+ else
+ return 0;
+}
+
+
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+ struct icmpv6hdr *icmph;
+ struct raw6_opt *opt;
+
+ opt = &sk->tp_pinfo.tp_raw;
+ icmph = (struct icmpv6hdr *) (skb->ipv6_hdr + 1);
+ return test_bit(icmph->type, &opt->filter);
+}
+
+/*
+ * demultiplex raw sockets.
+ * (should consider queueing the skb in the sock receive_queue
+ * without calling rawv6.c)
+ */
+static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+ struct device *dev,
+ struct ipv6_options *opt,
+ __u16 nexthdr,
+ __u16 len,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr)
+{
+ struct sock *sk, *sk2;
+ __u8 hash;
+
+ hash = nexthdr & (SOCK_ARRAY_SIZE-1);
+
+ sk = rawv6_prot.sock_array[hash];
+
+
+ /*
+ * The first socket found will be delivered after
+ * delivery to transport protocols.
+ */
+
+ if (sk == NULL)
+ return NULL;
+
+ sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr);
+
+ if (sk)
+ {
+ sk2 = sk;
+
+ while ((sk2 = inet6_get_sock_raw(sk2->next, nexthdr,
+ daddr, saddr)))
+ {
+ struct sk_buff *buff;
+
+ if (nexthdr == IPPROTO_ICMPV6 &&
+ icmpv6_filter(sk2, skb))
+ {
+ continue;
+ }
+ buff = skb_clone(skb, GFP_ATOMIC);
+ buff->sk = sk2;
+ rawv6_rcv(buff, dev, saddr, daddr, opt, len);
+ }
+ }
+
+ if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+ {
+ sk = NULL;
+ }
+
+ return sk;
+}
+
+int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct inet6_ifaddr *ifp;
+ struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
+ struct ipv6hdr *hdr;
+ u8 hash;
+ u8 addr_type;
+ struct inet6_protocol *ipprot;
+ struct sock *raw_sk;
+ int found = 0;
+ int nexthdr = 0;
+ __u8 *nhptr;
+ int pkt_len;
+
+ hdr = skb->ipv6_hdr = (struct ipv6hdr *) skb->h.raw;
+
+ if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+ {
+ ipv6_statistics.Ip6InHdrErrors++;
+ printk(KERN_DEBUG "ipv6_rcv: broken header\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ pkt_len = ntohs(hdr->payload_len);
+
+ if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+ {
+ printk(KERN_DEBUG "ipv6_rcv: invalid payload length\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+
+ /* check daddr */
+
+ /* Accounting & Firewall check */
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (addr_type & IPV6_ADDR_MULTICAST)
+ {
+ /*
+ * if mcast address is not for one of our groups
+ * either pass it to mcast router or discard it
+ */
+
+ if (ipv6_chk_mcast_addr(dev, &hdr->daddr) == 0)
+ {
+ /* something like:
+ if (acting_as_router)
+ ipv6_mcast_route(skb, ...)
+ else
+ */
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+
+ if (addr_type & IPV6_ADDR_MULTICAST ||
+ (ifp = ipv6_chk_addr(&hdr->daddr)))
+ {
+
+ /* loop in a cicle parsing nexthdrs */
+
+ skb->h.raw += sizeof(struct ipv6hdr);
+
+ /* extension header processing must update skb->h.raw */
+
+ nexthdr = hdr->nexthdr;
+ nhptr = &hdr->nexthdr;
+
+
+ while(1)
+ {
+ struct hdrtype_proc *hdrt;
+
+ /* check for extension header */
+
+ for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++)
+ {
+ if (hdrt->type == nexthdr)
+ {
+ if ((nexthdr = hdrt->func(&skb, dev, nhptr, opt)))
+ {
+ nhptr = skb->h.raw;
+ hdr = skb->ipv6_hdr;
+ continue;
+ }
+ return 0;
+ }
+ }
+ break;
+
+ }
+
+ /*
+ * deliver to raw sockets
+ * should we deliver raw after or before parsing
+ * extension headers ?
+ * delivering after means we do reassembly of datagrams
+ * in ip.
+ */
+
+ pkt_len = skb->tail - skb->h.raw;
+
+ raw_sk = ipv6_raw_deliver(skb, dev, opt, nexthdr, pkt_len,
+ &hdr->saddr, &hdr->daddr);
+
+ /* check inet6_protocol list */
+
+ hash = nexthdr & (MAX_INET_PROTOS -1);
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot = (struct inet6_protocol *) ipprot->next)
+ {
+ struct sk_buff *buff = skb;
+
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->copy || raw_sk)
+ buff = skb_clone(skb, GFP_ATOMIC);
+
+
+ ipprot->handler(buff, dev,
+ &hdr->saddr, &hdr->daddr,
+ opt, pkt_len,
+ 0, ipprot);
+ found = 1;
+ }
+
+ if (raw_sk)
+ {
+ skb->sk = raw_sk;
+ rawv6_rcv(skb, dev, &hdr->saddr, &hdr->daddr, opt,
+ htons(hdr->payload_len));
+ found = 1;
+ }
+
+ /* not found: send ICMP parameter problem back */
+
+ if (!found)
+ {
+ printk(KERN_DEBUG "proto not found %d\n", nexthdr);
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ }
+
+ }
+ else
+ {
+ if (ipv6_forwarding)
+ {
+ if (addr_type & IPV6_ADDR_LINKLOCAL)
+ {
+ printk(KERN_DEBUG
+ "link local pkt to forward\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ ipv6_forward(skb, dev, 0);
+ }
+ else
+ {
+ printk(KERN_WARNING "IPV6: packet to forward -"
+ "host not configured as router\n");
+ kfree_skb(skb, FREE_READ);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Local variables:
+ * c-file-style: "Linux"
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov