📄 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.65 2001/10/02 02:22:36 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/version.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/icmpv6.h>#include <linux/brlock.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>#include <asm/uaccess.h>#include <asm/system.h>#ifdef MODULEstatic int unloadable = 0; /* XX: Turn to one when all is ok within the module for allowing unload */#endif#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115MODULE_AUTHOR("Cast of dozens");MODULE_DESCRIPTION("IPv6 protocol stack for Linux");MODULE_PARM(unloadable, "i");#endif/* IPv6 procfs goodies... */#ifdef CONFIG_PROC_FSextern int raw6_get_info(char *, char **, off_t, int);extern int tcp6_get_info(char *, char **, off_t, int);extern int udp6_get_info(char *, char **, off_t, int);extern int afinet6_get_info(char *, char **, off_t, int);extern int afinet6_get_snmp(char *, char **, off_t, int);#endif#ifdef CONFIG_SYSCTLextern void ipv6_sysctl_register(void);extern void ipv6_sysctl_unregister(void);#endif#ifdef INET_REFCNT_DEBUGatomic_t inet6_sock_nr;#endif/* The inetsw table contains everything that inet_create needs to * build a new socket. */struct list_head inetsw6[SOCK_MAX];static void inet6_sock_destruct(struct sock *sk){ inet_sock_destruct(sk);#ifdef INET_REFCNT_DEBUG atomic_dec(&inet6_sock_nr);#endif MOD_DEC_USE_COUNT;}static int inet6_create(struct socket *sock, int protocol){ struct sock *sk; struct list_head *p; struct inet_protosw *answer; sk = sk_alloc(PF_INET6, GFP_KERNEL, 1); if (sk == NULL) goto do_oom; /* Look for the requested type/protocol pair. */ answer = NULL; br_read_lock_bh(BR_NETPROTO_LOCK); list_for_each(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; } br_read_unlock_bh(BR_NETPROTO_LOCK); if (!answer) goto free_and_badtype; if (answer->capability > 0 && !capable(answer->capability)) goto free_and_badperm; if (!protocol) goto free_and_noproto; sock->ops = answer->ops; sock_init_data(sock, sk); sk->prot = answer->prot; sk->no_check = answer->no_check; if (INET_PROTOSW_REUSE & answer->flags) sk->reuse = 1; if (SOCK_RAW == sock->type) { sk->num = protocol; if (IPPROTO_RAW == protocol) sk->protinfo.af_inet.hdrincl = 1; } sk->destruct = inet6_sock_destruct; sk->zapped = 0; sk->family = PF_INET6; sk->protocol = protocol; sk->backlog_rcv = answer->prot->backlog_rcv; sk->net_pinfo.af_inet6.hop_limit = -1; sk->net_pinfo.af_inet6.mcast_hops = -1; sk->net_pinfo.af_inet6.mc_loop = 1; sk->net_pinfo.af_inet6.pmtudisc = IPV6_PMTUDISC_WANT; /* Init the ipv4 part of the socket since we can have sockets * using v6 API for ipv4. */ sk->protinfo.af_inet.ttl = 64; sk->protinfo.af_inet.mc_loop = 1; sk->protinfo.af_inet.mc_ttl = 1; sk->protinfo.af_inet.mc_index = 0; sk->protinfo.af_inet.mc_list = NULL; if (ipv4_config.no_pmtu_disc) sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_DONT; else sk->protinfo.af_inet.pmtudisc = IP_PMTUDISC_WANT;#ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); atomic_inc(&inet_sock_nr);#endif MOD_INC_USE_COUNT; if (sk->num) { /* It assumes that any protocol which allows * the user to assign a number at socket * creation time automatically shares. */ sk->sport = ntohs(sk->num); sk->prot->hash(sk); } if (sk->prot->init) { int err = sk->prot->init(sk); if (err != 0) { MOD_DEC_USE_COUNT; inet_sock_release(sk); return err; } } return 0;free_and_badtype: sk_free(sk); return -ESOCKTNOSUPPORT;free_and_badperm: sk_free(sk); return -EPERM;free_and_noproto: sk_free(sk); return -EPROTONOSUPPORT;do_oom: return -ENOBUFS;}/* bind for INET6 API */static 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; __u32 v4addr = 0; unsigned short snum; int addr_type = 0; /* If the socket has its own bind function then use it. */ if(sk->prot->bind) return 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; /* 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) return -EADDRNOTAVAIL; } else { if (addr_type != IPV6_ADDR_ANY) { /* ipv4 addr of the socket is invalid. Only the * unpecified and mapped address have a v4 equivalent. */ v4addr = LOOPBACK4_IPV6; if (!(addr_type & IPV6_ADDR_MULTICAST)) { if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) return -EADDRNOTAVAIL; } } } 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->state != TCP_CLOSE) || (sk->num != 0)) { release_sock(sk); return -EINVAL; } 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->bound_dev_if = addr->sin6_scope_id; } /* Binding to link-local address requires an interface */ if (sk->bound_dev_if == 0) { release_sock(sk); return -EINVAL; } } sk->rcv_saddr = v4addr; sk->saddr = v4addr; ipv6_addr_copy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr); if (!(addr_type & IPV6_ADDR_MULTICAST)) ipv6_addr_copy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr); /* Make sure we are allowed to bind here. */ if (sk->prot->get_port(sk, snum) != 0) { sk->rcv_saddr = 0; sk->saddr = 0; memset(&sk->net_pinfo.af_inet6.rcv_saddr, 0, sizeof(struct in6_addr)); memset(&sk->net_pinfo.af_inet6.saddr, 0, sizeof(struct in6_addr)); release_sock(sk); return -EADDRINUSE; } if (addr_type != IPV6_ADDR_ANY) sk->userlocks |= SOCK_BINDADDR_LOCK; if (snum) sk->userlocks |= SOCK_BINDPORT_LOCK; sk->sport = ntohs(sk->num); sk->dport = 0; sk->daddr = 0; release_sock(sk); return 0;}static int inet6_release(struct socket *sock){ struct sock *sk = sock->sk; if (sk == NULL) return -EINVAL; /* Free mc lists */ ipv6_sock_mc_close(sk); return inet_release(sock);}int inet6_destroy_sock(struct sock *sk){ struct sk_buff *skb; struct ipv6_txoptions *opt; /* * Release destination entry */ sk_dst_reset(sk); /* Release rx options */ if ((skb = xchg(&sk->net_pinfo.af_inet6.pktoptions, NULL)) != NULL) kfree_skb(skb); /* Free flowlabels */ fl6_free_socklist(sk); /* Free tx options */ if ((opt = xchg(&sk->net_pinfo.af_inet6.opt, NULL)) != NULL)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -