📄 af_inet6.c
字号:
/* * PF_INET6 socket protocol family * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * Adapted from linux/net/ipv4/af_inet.c * * $Id: af_inet6.c,v 1.66 2002/02/01 22:01:04 davem Exp $ * * Fixes: * piggy, Karl Knutson : Socket protocol table * Hideaki YOSHIFUJI : sin6_scope_id support * Arnaldo Melo : check proc_net_create return, cleanups * * 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/module.h>#include <linux/config.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/kernel.h>#include <linux/major.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/icmpv6.h>#include <linux/smp_lock.h>#include <net/ip.h>#include <net/ipv6.h>#include <net/udp.h>#include <net/tcp.h>#include <net/ipip.h>#include <net/protocol.h>#include <net/inet_common.h>#include <net/transp_v6.h>#include <net/ip6_route.h>#include <net/addrconf.h>#ifdef CONFIG_IPV6_TUNNEL#include <net/ip6_tunnel.h>#endif#include <asm/uaccess.h>#include <asm/system.h>MODULE_AUTHOR("Cast of dozens");MODULE_DESCRIPTION("IPv6 protocol stack for Linux");MODULE_LICENSE("GPL");/* IPv6 procfs goodies... */#ifdef CONFIG_PROC_FSextern int raw6_proc_init(void);extern void raw6_proc_exit(void);extern int tcp6_proc_init(void);extern void tcp6_proc_exit(void);extern int udp6_proc_init(void);extern void udp6_proc_exit(void);extern int ipv6_misc_proc_init(void);extern void ipv6_misc_proc_exit(void);extern int ac6_proc_init(void);extern void ac6_proc_exit(void);extern int if6_proc_init(void);extern void if6_proc_exit(void);#endifint sysctl_ipv6_bindv6only;#ifdef INET_REFCNT_DEBUGatomic_t inet6_sock_nr;#endif/* The inetsw table contains everything that inet_create needs to * build a new socket. */static struct list_head inetsw6[SOCK_MAX];static spinlock_t inetsw6_lock = SPIN_LOCK_UNLOCKED;static void inet6_sock_destruct(struct sock *sk){ inet_sock_destruct(sk);#ifdef INET_REFCNT_DEBUG atomic_dec(&inet6_sock_nr);#endif}static __inline__ struct ipv6_pinfo *inet6_sk_generic(struct sock *sk){ const int offset = sk->sk_prot->slab_obj_size - sizeof(struct ipv6_pinfo); return (struct ipv6_pinfo *)(((u8 *)sk) + offset);}static int inet6_create(struct socket *sock, int protocol){ struct inet_opt *inet; struct ipv6_pinfo *np; struct sock *sk; struct tcp6_sock* tcp6sk; struct list_head *p; struct inet_protosw *answer; struct proto *answer_prot; unsigned char answer_flags; char answer_no_check; int rc; /* Look for the requested type/protocol pair. */ answer = NULL; rcu_read_lock(); list_for_each_rcu(p, &inetsw6[sock->type]) { answer = list_entry(p, struct inet_protosw, list); /* Check the non-wild match. */ if (protocol == answer->protocol) { if (protocol != IPPROTO_IP) break; } else { /* Check for the two wild cases. */ if (IPPROTO_IP == protocol) { protocol = answer->protocol; break; } if (IPPROTO_IP == answer->protocol) break; } answer = NULL; } rc = -ESOCKTNOSUPPORT; if (!answer) goto out_rcu_unlock; rc = -EPERM; if (answer->capability > 0 && !capable(answer->capability)) goto out_rcu_unlock; rc = -EPROTONOSUPPORT; if (!protocol) goto out_rcu_unlock; sock->ops = answer->ops; answer_prot = answer->prot; answer_no_check = answer->no_check; answer_flags = answer->flags; rcu_read_unlock(); BUG_TRAP(answer_prot->slab != NULL); rc = -ENOBUFS; sk = sk_alloc(PF_INET6, GFP_KERNEL, answer_prot->slab_obj_size, answer_prot->slab); if (sk == NULL) goto out; sock_init_data(sock, sk); sk_set_owner(sk, THIS_MODULE); rc = 0; sk->sk_prot = answer_prot; sk->sk_no_check = answer_no_check; if (INET_PROTOSW_REUSE & answer_flags) sk->sk_reuse = 1; inet = inet_sk(sk); if (SOCK_RAW == sock->type) { inet->num = protocol; if (IPPROTO_RAW == protocol) inet->hdrincl = 1; } sk->sk_destruct = inet6_sock_destruct; sk->sk_family = PF_INET6; sk->sk_protocol = protocol; sk->sk_backlog_rcv = answer->prot->backlog_rcv; tcp6sk = (struct tcp6_sock *)sk; tcp6sk->pinet6 = np = inet6_sk_generic(sk); np->hop_limit = -1; np->mcast_hops = -1; np->mc_loop = 1; np->pmtudisc = IPV6_PMTUDISC_WANT; np->ipv6only = sysctl_ipv6_bindv6only; /* Init the ipv4 part of the socket since we can have sockets * using v6 API for ipv4. */ inet->uc_ttl = -1; inet->mc_loop = 1; inet->mc_ttl = 1; inet->mc_index = 0; inet->mc_list = NULL; if (ipv4_config.no_pmtu_disc) inet->pmtudisc = IP_PMTUDISC_DONT; else inet->pmtudisc = IP_PMTUDISC_WANT;#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); atomic_inc(&inet_sock_nr);#endif if (inet->num) { /* It assumes that any protocol which allows * the user to assign a number at socket * creation time automatically shares. */ inet->sport = ntohs(inet->num); sk->sk_prot->hash(sk); } if (sk->sk_prot->init) { rc = sk->sk_prot->init(sk); if (rc) { sk_common_release(sk); goto out; } }out: return rc;out_rcu_unlock: rcu_read_unlock(); goto out;}/* bind for INET6 API */int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len){ struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr; struct sock *sk = sock->sk; struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); __u32 v4addr = 0; unsigned short snum; int addr_type = 0; int err = 0; /* If the socket has its own bind function then use it. */ if (sk->sk_prot->bind) return sk->sk_prot->bind(sk, uaddr, addr_len); if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; addr_type = ipv6_addr_type(&addr->sin6_addr); if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; snum = ntohs(addr->sin6_port); if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) return -EACCES; lock_sock(sk); /* Check these errors (active socket, double bind). */ if (sk->sk_state != TCP_CLOSE || inet->num) { err = -EINVAL; goto out; } /* Check if the address belongs to the host. */ if (addr_type == IPV6_ADDR_MAPPED) { v4addr = addr->sin6_addr.s6_addr32[3]; if (inet_addr_type(v4addr) != RTN_LOCAL) { err = -EADDRNOTAVAIL; goto out; } } else { if (addr_type != IPV6_ADDR_ANY) { struct net_device *dev = NULL; if (addr_type & IPV6_ADDR_LINKLOCAL) { if (addr_len >= sizeof(struct sockaddr_in6) && addr->sin6_scope_id) { /* Override any existing binding, if another one * is supplied by user. */ sk->sk_bound_dev_if = addr->sin6_scope_id; } /* Binding to link-local address requires an interface */ if (!sk->sk_bound_dev_if) { err = -EINVAL; goto out; } dev = dev_get_by_index(sk->sk_bound_dev_if); if (!dev) { err = -ENODEV; goto out; } } /* ipv4 addr of the socket is invalid. Only the * unspecified and mapped address have a v4 equivalent. */ v4addr = LOOPBACK4_IPV6; if (!(addr_type & IPV6_ADDR_MULTICAST)) { if (!ipv6_chk_addr(&addr->sin6_addr, dev, 0)) { if (dev) dev_put(dev); err = -EADDRNOTAVAIL; goto out; } } if (dev) dev_put(dev); } } inet->rcv_saddr = v4addr; inet->saddr = v4addr; ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr); if (!(addr_type & IPV6_ADDR_MULTICAST)) ipv6_addr_copy(&np->saddr, &addr->sin6_addr); /* Make sure we are allowed to bind here. */ if (sk->sk_prot->get_port(sk, snum)) { inet_reset_saddr(sk); err = -EADDRINUSE; goto out; } if (addr_type != IPV6_ADDR_ANY) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; inet->sport = ntohs(inet->num); inet->dport = 0; inet->daddr = 0;out: release_sock(sk); return err;}int inet6_release(struct socket *sock){ struct sock *sk = sock->sk; if (sk == NULL) return -EINVAL; /* Free mc lists */ ipv6_sock_mc_close(sk); /* Free ac lists */ ipv6_sock_ac_close(sk); return inet_release(sock);}int inet6_destroy_sock(struct sock *sk){ struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; struct ipv6_txoptions *opt; /* * Release destination entry */ sk_dst_reset(sk); /* Release rx options */ if ((skb = xchg(&np->pktoptions, NULL)) != NULL) kfree_skb(skb); /* Free flowlabels */ fl6_free_socklist(sk); /* Free tx options */ if ((opt = xchg(&np->opt, NULL)) != NULL) sock_kfree_s(sk, opt, opt->tot_len); return 0;}/* * This does both peername and sockname. */ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer){ struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr; struct sock *sk = sock->sk; struct inet_opt *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); sin->sin6_family = AF_INET6; sin->sin6_flowinfo = 0; sin->sin6_scope_id = 0; if (peer) { if (!inet->dport) return -ENOTCONN; if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) && peer == 1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -