af_inet6.c:

static int __init inet6_init(void)
	struct sk_buff *dummy_skb;
        struct list_head *r;
	int err;
- if (sizeof(struct inet6_skb_parm) > sizeof(dummy_skb->cb)) {
  - printk(KERN_CRIT "inet6_proto_init: size fault\n");
  - return -EINVAL; }

- for(r = &inetsw6[0]; r < &inetsw6[SOCK_MAX]; ++r)	: /* Register the socket-side information for inet6_create.  */
  - INIT_LIST_HEAD(r);

- inet6_register_protosw(&rawv6_protosw);		: /* We MUST register RAW sockets before we create the ICMP6,
							:  * IGMP6, or NDISC control sockets. */
static struct inet_protosw rawv6_protosw = {
	type:        SOCK_RAW,
	protocol:    IPPROTO_IP,	/* wild card */
	prot:        &rawv6_prot,
	ops:         &inet6_dgram_ops,
	capability:  CAP_NET_RAW,
	no_check:    UDP_CSUM_DEFAULT,
	flags:       INET_PROTOSW_REUSE,
};

----------------------------
raw.c:
struct proto rawv6_prot = {
	name:		"RAW",
	close:		rawv6_close,
	connect:	udpv6_connect,
	disconnect:	udp_disconnect,
	ioctl:		rawv6_ioctl,
	init:		rawv6_init_sk,
	destroy:	inet6_destroy_sock,
	setsockopt:	rawv6_setsockopt,
	getsockopt:	rawv6_getsockopt,
	sendmsg:	rawv6_sendmsg,
	recvmsg:	rawv6_recvmsg,
	bind:		rawv6_bind,
	backlog_rcv:	rawv6_rcv_skb,
	hash:		raw_v6_hash,
	unhash:		raw_v6_unhash,
};

static int rawv6_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
	struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
	int val;
- switch(level) {					: SOL_RAW  SOL_ICMPV6  SOL_IPV6 + IPV6_CHECKSUM ʳ ipv6_setsockopt() 
  - case SOL_RAW: break;
  - case SOL_ICMPV6:
    - if (sk->num != IPPROTO_ICMPV6) return -EOPNOTSUPP;
    - return rawv6_seticmpfilter(sk, level, optname, optval, optlen);
  - case SOL_IPV6:
    - if (optname == IPV6_CHECKSUM) break;
  - default:
    - return ipv6_setsockopt(sk, level, optname, optval, optlen); };

----------------------------
ipv6_sockglue.c:

int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	int val, valbool;
	int retv = -ENOPROTOOPT;
- if(level==SOL_IP && sk->type != SOCK_RAW)
  - return udp_prot.setsockopt(sk, level, optname, optval, optlen);
- if(level!=SOL_IPV6) goto out;
- if (optlen < 0) goto e_inval;
- if (optval == NULL) val=0;
- else if (get_user(val, (int *) optval) && get_user(val, (u8 *) optval))
  - return -EFAULT;
- valbool = (val!=0);
- lock_sock(sk);
- switch (optname) {
  - case IPV6_PKTINFO:
    - if (optlen != sizeof(int)) goto e_inval;
    - np->rxopt.bits.rxinfo = valbool;
    - retv = 0; break;
  - case IPV6_FLOWINFO:
    - if (optlen != sizeof(int)) goto e_inval;
    - np->rxopt.bits.rxflow = valbool;				: rxinfo  rxflow  datagram_recv_ctl() ǸƤӽФ
    - retv = 0; break;						: put_cmsg(msg, SOL_IPV6, ...) ǽ

  - case IPV6_PKTOPTIONS:					: IPsec ط
    {
    - struct ipv6_txoptions *opt = NULL;
    - struct msghdr msg;
    - struct flowi fl;
    - int junk;

    - fl.fl6_flowlabel = 0; fl.oif = sk->bound_dev_if;
    - if (!optval || optlen == 0) goto update;
		/* 1K is probably excessive
		 * 1K is surely not enough, 2K per standard header is 16K.
		 */
    - retv = -EINVAL;
    - if (optlen > 64*1024) break;

    - opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
    - retv = -ENOBUFS;
    - if (opt == NULL) break;

    - memset(opt, 0, sizeof(*opt));
    - opt->tot_len = sizeof(*opt) + optlen;
    - retv = -EFAULT;
    - if (copy_from_user(opt+1, optval, optlen)) goto done;

    - msg.msg_controllen = optlen;
    - msg.msg_control = (void*)(opt+1);

    - retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk);
    - if (retv) goto done;
    update:
    - retv = 0;
    - if (sk->type == SOCK_STREAM) {
      - if (opt) {
	- struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
	- if (!((1<<sk->state)&(TCPF_LISTEN|TCPF_CLOSE)) && sk->daddr != LOOPBACK4_IPV6) {
	#ifdef CONFIG_IPV6_IPSEC
	  - struct ipsec_sp *policy_ptr = NULL;
	  - int action = ipsec6_output_check(sk,NULL,NULL,&policy_ptr);
	#endif /* CONFIG_IPV6_IPSEC */
	  - tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
	#ifdef CONFIG_IPV6_IPSEC
	/* StS: Adapt ext_header_len according to AH/ESP size */
          - if (action != IPSEC_ACTION_DROP) {
            - printk(KERN_DEBUG "ipv6_setsockopt: ipsec6_output_check said DROP.\n");
            - if (opt->auth) {
              - action &=! IPSEC_ACTION_AUTH;
          - } else {
              - tp->ext_header_len += ipsec6_out_get_hdrsize(policy_ptr);
          - }
        - }
        - ipsec6_out_finish(NULL, policy_ptr);
					/* StS end */
	#endif /* CONFIG_IPV6_IPSEC */
        - tcp_sync_mss(sk, tp->pmtu_cookie);
      - }
    - }
    - opt = xchg(&np->opt, opt);
    - sk_dst_reset(sk);
    - } else {
      - write_lock(&sk->dst_lock);
      - opt = xchg(&np->opt, opt);
      - write_unlock(&sk->dst_lock);
      - sk_dst_reset(sk);
    - }

    done:
    - if (opt)
      - sock_kfree_s(sk, opt, opt->tot_len); break;
    }
  - case IPV6_FLOWINFO_SEND:					: sndflow  inet6_getname()  ipv6_recv_error() ǸƤӽФ졢
								: sin->sin6_flowinfo  flow_label ֤
								: tcp_v6_connect(), rawv6_sendmsg(), udpv6_connect(), udpv6_sendmsg() Ǥ
								: fl6_sock_lookup()  dst õ
    - if (optlen != sizeof(int)) goto e_inval;
    - np->sndflow = valbool;
    - retv = 0; break;
  - case IPV6_FLOWLABEL_MGR:					: optval ͤˤäưۤʤ
    - retv = ipv6_flowlabel_opt(sk, optval, optlen); break;
  - case IPV6_RECVTCLASS:
    - if (optlen != sizeof(int)) goto e_inval;
    - np->recvopt.tclass = valbool;
    - retv = 0; break;
  - case IPV6_TCLASS:
    - if (optlen != sizeof(int)) goto e_inval;
    - np->tclass = val;
    - retv = 0; break;


datagram.c:
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
- if (np->rxopt.bits.rxinfo) {
  - struct in6_pktinfo src_info;
  - src_info.ipi6_ifindex = opt->iif;
  - ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
  - put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); }

- if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) {
  - u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK;
  - put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); }

- if (np->rxopt.bits.hopopts && opt->hop) {
  - u8 *ptr = skb->nh.raw + opt->hop;
  - put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); }

- if (np->rxopt.bits.dstopts && opt->dst0) {
  - u8 *ptr = skb->nh.raw + opt->dst0;
  - put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); }

- if (np->rxopt.bits.srcrt && opt->srcrt) {
  - struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt);
  - put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); }

- if (np->rxopt.bits.authhdr && opt->auth) {
  - u8 *ptr = skb->nh.raw + opt->auth;
  - put_cmsg(msg, SOL_IPV6, IPV6_AUTHHDR, (ptr[1]+1)<<2, ptr); }

- if (np->rxopt.bits.dstopts && opt->dst1) {
  - u8 *ptr = skb->nh.raw + opt->dst1;
  - put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); }

- if (np->recvopt.tclass) {
  - struct ipv6hdr *ipv6h = skb->nh.ipv6h;
  - int tclass = ((ipv6h->tclass1 << 4) | (ipv6h->tclass2_flow[0] >> 4)) & 0xff;
  - put_cmsg(msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass); }

scm.c:
int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
	cm(=msg->msg_control)Υإå cmhdr.cmsg_level/_type/_len  level/type/sizeof(cmhdr)+len 򤤤ơ
	ǡʬ CMSG_DATA(cm)  data 롣ͤ 0  -EFAULT(ԡǤʤäȤ)

ip6_flowlabel.c:
int ipv6_flowlabel_opt(struct sock *sk, char *optval, int optlen)
{
	int err;
	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
	struct in6_flowlabel_req freq;
	struct ipv6_fl_socklist *sfl1=NULL;
	struct ipv6_fl_socklist *sfl, **sflp;
	struct ip6_flowlabel *fl;

- if (copy_from_user(&freq, optval, sizeof(freq))) return -EFAULT;
- switch (freq.flr_action) {
  - case IPV6_FL_A_PUT:							: sk ϥ٥뤬Ȥ뤬MGR ϥ٥äޤޤˤʤ롣
    - write_lock_bh(&ip6_sk_fl_lock);
    - for (sflp = &np->ipv6_fl_list; (sfl=*sflp)!=NULL; sflp = &sfl->next) {	: /proc/net/ip6_flowlabel ƤΤ fl_ht[] 
									: sk->net_pinfo.af_inet6->ipv6_fl_list ǤϤʤ
      - if (sfl->fl->label == freq.flr_label) {
        - if (freq.flr_label == (np->flow_label&IPV6_FLOWLABEL_MASK)) np->flow_label &= ~IPV6_FLOWLABEL_MASK;
        - *sflp = sfl->next;
        - write_unlock_bh(&ip6_sk_fl_lock);
        - fl_release(sfl->fl);
        - kfree(sfl);
        - return 0;
      - }
    - }
    - write_unlock_bh(&ip6_sk_fl_lock);
    - return -ESRCH;

  - case IPV6_FL_A_RENEW:
    - read_lock_bh(&ip6_sk_fl_lock);
    - for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {
      - if (sfl->fl->label == freq.flr_label) {
        - err = fl6_renew(sfl->fl, freq.flr_linger, freq.flr_expires);		: linger  expires ι
        - read_unlock_bh(&ip6_sk_fl_lock);
        - return err;
      - }
    - }
    - read_unlock_bh(&ip6_sk_fl_lock);
    - if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {	: NONE ϻȤƤʤΤǤʬϼ¹Ԥʤ
      - fl = fl_lookup(freq.flr_label);					: fl_ht[FL_HASH(label)] ٥õ
									: դä fl->users  1ä fl ֤
      - if (fl) {
        - err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
        - fl_release(fl);						: fl_lookup ʬ򸺤餹
        - return err;
      - }
    - }
    - return -ESRCH;

  - case IPV6_FL_A_GET:
    - if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL;
    - fl = fl_create(&freq, optval, optlen, &err);		: kmalloc Ǻ fl->share ˤä fl->owner ꤹ
    - if (fl == NULL) return err;
    - sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL);
    - if (freq.flr_label) {
      - struct ip6_flowlabel *fl1 = NULL;
      - err = -EEXIST;
      - read_lock_bh(&ip6_sk_fl_lock);
      - for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) {	: sk Υ٥ΥꥹȤ¸ߤ뤫ɤ
        - if (sfl->fl->label == freq.flr_label) {
          - if (freq.flr_flags&IPV6_FL_F_EXCL) {
            - read_unlock_bh(&ip6_sk_fl_lock);
            - goto done;
          - }
          - fl1 = sfl->fl;
          - atomic_inc(&fl->users);
          - break;
        - }
      - }
      - read_unlock_bh(&ip6_sk_fl_lock);
      - if (fl1 == NULL) fl1 = fl_lookup(freq.flr_label);	: sk ΥꥹȤ̵ä
      - if (fl1) {
        - err = -EEXIST;
        - if (freq.flr_flags&IPV6_FL_F_EXCL) goto release;
        - err = -EPERM;
        - if (fl1->share == IPV6_FL_S_EXCL || fl1->share != fl->share || fl1->owner != fl->owner) goto release;
        - err = -EINVAL;
        - if (ipv6_addr_cmp(&fl1->dst, &fl->dst) || ipv6_opt_cmp(fl1->opt, fl->opt)) goto release;
        - err = -ENOMEM;
        - if (sfl1 == NULL) goto release;
        - if (fl->linger > fl1->linger) fl1->linger = fl->linger;
        - if ((long)(fl->expires - fl1->expires) > 0) fl1->expires = fl->expires;
        - write_lock_bh(&ip6_sk_fl_lock);
        - sfl1->fl = fl1;
        - sfl1->next = np->ipv6_fl_list;			: sk ΥꥹȤƬ˺ե٥ɲ
        - np->ipv6_fl_list = sfl1;
        - write_unlock_bh(&ip6_sk_fl_lock);
        - fl_free(fl);
        - return 0;
release:
        - fl_release(fl1);
        - goto done;
      - }
    - }
    - err = -ENOENT;
    - if (!(freq.flr_flags&IPV6_FL_F_CREATE)) goto done;
    - err = -ENOMEM;
    - if (sfl1 == NULL || (err = mem_check(sk)) != 0) goto done;
    - err = fl_intern(fl, freq.flr_label);			: fl_ht[FL_HASH(label)] Ƭ fl Ͽ
    - if (err) goto done;
/* Do not check for fault */
    - if (!freq.flr_label) copy_to_user(optval + ((u8*)&freq.flr_label - (u8*)&freq), &fl->label, sizeof(fl->label));
    - sfl1->fl = fl;
    - sfl1->next = np->ipv6_fl_list;				: sk ΥꥹȤƬ˺ե٥ɲ
    - np->ipv6_fl_list = sfl1;
    - return 0;

  - default:
    - return -EINVAL;
- }

done:
- if (fl) fl_free(fl);
- if (sfl1) kfree(sfl1);
- return err;
}


void ip6_flowlabel_init()					: ip6_fl_gc_timer ν /proc/net/ip6_flowlabel ν
{
  - init_timer(&ip6_fl_gc_timer);
  - ip6_fl_gc_timer.function = ip6_fl_gc;
#ifdef CONFIG_PROC_FS
  - create_proc_read_entry("net/ip6_flowlabel", 0, 0, ip6_fl_read_proc, NULL);
#endif
}


static int ip6_fl_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
								: fl_ht[0 - FL_HASH_MASK] Υե٥ɽ
{
	off_t pos=0;
	off_t begin=0;
	int len=0;
	int i, k;
	struct ip6_flowlabel *fl;

- len+= sprintf(buffer,"Label S Owner  Users  Linger Expires  " "Dst                              Opt\n");
- read_lock_bh(&ip6_fl_lock);
- for (i=0; i<=FL_HASH_MASK; i++) {
  - for (fl = fl_ht[i]; fl; fl = fl->next) {
    - len+=sprintf(buffer+len,"%05X %-1d %-6d %-6d %-6d %-8ld ",			: ե٥°ɽ
		(unsigned)ntohl(fl->label), fl->share, (unsigned)fl->owner,
		atomic_read(&fl->users), fl->linger/HZ, (long)(fl->expires - jiffies)/HZ);
    - for (k=0; k<16; k++) len+=sprintf(buffer+len, "%02x", fl->dst.s6_addr[k]);	: dst_addr ɽ
    - buffer[len++]=' ';
    - len+=sprintf(buffer+len, "%-4d", fl->opt ? fl->opt->opt_nflen : 0);
    - buffer[len++]='\n';
    - pos=begin+len;
    - if(pos<offset) {
      - len=0;
      - begin=pos;
    - }
    - if(pos>offset+length) goto done;
  - }
- }
- *eof = 1;

done:
- read_unlock_bh(&ip6_fl_lock);
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length) len=length;
- if(len<0) len=0;
- return len;
}

static void fl_release(struct ip6_flowlabel *fl)		: fl->users 򸺤餷 0 ʤ ip6_fl_gc_timer.expire ι
{
- fl->lastuse = jiffies;
- if (atomic_dec_and_test(&fl->users)) {			: 1餷 0 ʤ true ֤
  - unsigned long ttd = fl->lastuse + fl->linger;
  - if ((long)(ttd - fl->expires) > 0) fl->expires = ttd;
  - ttd = fl->expires;
  - if (fl->opt && fl->share == IPV6_FL_S_EXCL) {
    - struct ipv6_txoptions *opt = fl->opt;
    - fl->opt = NULL;
    - kfree(opt);
  - }
  - if (!del_timer(&ip6_fl_gc_timer) ||				: timer.list.next  NULL Ǥʤ timer.list 
	(long)(ip6_fl_gc_timer.expires - ttd) > 0)
    - ip6_fl_gc_timer.expires = ttd;
  - add_timer(&ip6_fl_gc_timer);				: timer Ƥ뤳Ȥǧ timer.list  tv?.vec.prev դ
- }
}

static struct ip6_flowlabel *
fl_create(struct in6_flowlabel_req *freq, char *optval, int optlen, int *err_p)
{
	struct ip6_flowlabel *fl;
	int olen;
	int addr_type;
	int err;

- err = -ENOMEM;
- fl = kmalloc(sizeof(*fl), GFP_KERNEL);
- if (fl == NULL) goto done;
- memset(fl, 0, sizeof(*fl));

- olen = optlen - CMSG_ALIGN(sizeof(*freq));
- if (olen > 0) {
		struct msghdr msg;
		struct flowi flowi;
		int junk;

  - err = -ENOMEM;
  - fl->opt = kmalloc(sizeof(*fl->opt) + olen, GFP_KERNEL);
  - if (fl->opt == NULL) goto done;

  - memset(fl->opt, 0, sizeof(*fl->opt));
  - fl->opt->tot_len = sizeof(*fl->opt) + olen;
  - err = -EFAULT;
  - if (copy_from_user(fl->opt+1, optval+CMSG_ALIGN(sizeof(*freq)), olen)) goto done;

  - msg.msg_controllen = olen;
  - msg.msg_control = (void*)(fl->opt+1);
  - flowi.oif = 0;

  - err = datagram_send_ctl(&msg, &flowi, fl->opt, &junk, &junk);
  - if (err) goto done;
  - err = -EINVAL;
  - if (fl->opt->opt_flen) goto done;				: IPV6_DSTOPTS, IPV6_AUTHHDR, IPV6_RTHDR ˴ط
  - if (fl->opt->opt_nflen == 0) {				: IPV6_HOPOPTS, IPV6_RTHDR ˴ط
    - kfree(fl->opt);
    - fl->opt = NULL;
  - }
- }

- fl->expires = jiffies;
- err = fl6_renew(fl, freq->flr_linger, freq->flr_expires);
- if (err) goto done;
- fl->share = freq->flr_share;
- addr_type = ipv6_addr_type(&freq->flr_dst);
- if ((addr_type&IPV6_ADDR_MAPPED) || addr_type == IPV6_ADDR_ANY) goto done;
- ipv6_addr_copy(&fl->dst, &freq->flr_dst);
- atomic_set(&fl->users, 1);
- switch (fl->share) {
- case IPV6_FL_S_EXCL:
- case IPV6_FL_S_ANY:
  - break;
- case IPV6_FL_S_PROCESS:
  - fl->owner = current->pid;
  - break;
- case IPV6_FL_S_USER:
  - fl->owner = current->euid;
  - break;
- default:
  - err = -EINVAL;
  - goto done;
- }
- return fl;

done:
- if (fl) fl_free(fl);
- *err_p = err;
- return NULL;
}

int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
		      struct ipv6_txoptions *opt,
		      int *hlimit, int *tclass)
{
	struct in6_pktinfo *src_info;
	struct cmsghdr *cmsg;
	struct ipv6_rt_hdr *rthdr;
	struct ipv6_opt_hdr *hdr;
	int len;
	int err = 0;

- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
  - if (cmsg->cmsg_len < sizeof(struct cmsghdr) || 
	(unsigned long)(((char*)cmsg - (char*)msg->msg_control) + cmsg->cmsg_len) > msg->msg_controllen) {
    - err = -EINVAL;
    - goto exit_f;
  - }

  - if (cmsg->cmsg_level != SOL_IPV6) continue;

  - switch (cmsg->cmsg_type) {
  - case IPV6_PKTINFO:
    - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct in6_pktinfo))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - src_info = (struct in6_pktinfo *)CMSG_DATA(cmsg);
    - if (src_info->ipi6_ifindex) {
      - if (fl->oif && src_info->ipi6_ifindex != fl->oif) return -EINVAL;
      - fl->oif = src_info->ipi6_ifindex;
    - }
    - if (!ipv6_addr_any(&src_info->ipi6_addr)) {
      - if (!ipv6_chk_addr(&src_info->ipi6_addr, NULL)) {
        - err = -EINVAL;
        - goto exit_f;
      - }
      - fl->fl6_src = &src_info->ipi6_addr;
    - }
    - break;

  - case IPV6_FLOWINFO:
    - if (cmsg->cmsg_len < CMSG_LEN(4)) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - if (fl->fl6_flowlabel&IPV6_FLOWINFO_MASK) {
      - if ((fl->fl6_flowlabel^*(u32 *)CMSG_DATA(cmsg))&~IPV6_FLOWINFO_MASK) {
        - err = -EINVAL;
        - goto exit_f;
      - }
    - }
    - fl->fl6_flowlabel = IPV6_FLOWINFO_MASK & *(u32 *)CMSG_DATA(cmsg);
    - break;

  - case IPV6_HOPOPTS:
    - if (opt->hopopt || cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
    - len = ((hdr->hdrlen + 1) << 3);
    - if (cmsg->cmsg_len < CMSG_LEN(len)) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - if (!capable(CAP_NET_RAW)) {
      - err = -EPERM;
      - goto exit_f;
    - }
    - opt->opt_nflen += len;
    - opt->hopopt = hdr;
    - break;

  - case IPV6_DSTOPTS:
    - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
    - len = ((hdr->hdrlen + 1) << 3);
    - if (cmsg->cmsg_len < CMSG_LEN(len)) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - if (!capable(CAP_NET_RAW)) {
      - err = -EPERM;
      - goto exit_f;
    - }
    - if (opt->dst1opt) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - opt->opt_flen += len;
    - opt->dst1opt = hdr;
    - break;

  - case IPV6_AUTHHDR:
    - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_opt_hdr))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - hdr = (struct ipv6_opt_hdr *)CMSG_DATA(cmsg);
    - len = ((hdr->hdrlen + 2) << 2);
    - if (cmsg->cmsg_len < CMSG_LEN(len)) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - if (len & ~7) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - opt->opt_flen += len;
    - opt->auth = hdr;
    - break;

  - case IPV6_RTHDR:
    - if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ipv6_rt_hdr))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
	/*
	 *	TYPE 0
	 */
    - if (rthdr->type) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - len = ((rthdr->hdrlen + 1) << 3);
    - if (cmsg->cmsg_len < CMSG_LEN(len)) {
      - err = -EINVAL;
      - goto exit_f;
    - }
	/* segments left must also match */
    - if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - opt->opt_nflen += len;
    - opt->srcrt = rthdr;
    - if (opt->dst1opt) {
      - int dsthdrlen = ((opt->dst1opt->hdrlen+1)<<3);
      - opt->opt_nflen += dsthdrlen;
      - opt->dst0opt = opt->dst1opt;
      - opt->dst1opt = NULL;
      - opt->opt_flen -= dsthdrlen;
    - }
    - break;

  - case IPV6_HOPLIMIT:
    - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - *hlimit = *(int *)CMSG_DATA(cmsg);
    - break;

  - case IPV6_TCLASS:
    - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
      - err = -EINVAL;
      - goto exit_f;
    - }
    - *tclass = *(int *)CMSG_DATA(cmsg);
    - break;

  - default:
    - if (net_ratelimit())
      - printk(KERN_DEBUG "invalid cmsg type: %d\n", cmsg->cmsg_type);
    - err = -EINVAL;
    - break;
  - };
- }

exit_f:
- return err;
}

