📄 raw.c
字号:
/* * RAW sockets for IPv6 * Linux INET6 implementation * * Authors: * Pedro Roque <roque@di.fc.ul.pt> * * Adapted from linux/net/ipv4/raw.c * * $Id: raw.c,v 1.50 2001/09/18 22:29:10 davem Exp $ * * Fixes: * Hideaki YOSHIFUJI : sin6_scope_id support * * 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/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/icmpv6.h>#include <asm/uaccess.h>#include <asm/ioctls.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/transp_v6.h>#include <net/udp.h>#include <net/inet_common.h>#include <net/rawv6.h>struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];rwlock_t raw_v6_lock = RW_LOCK_UNLOCKED;static void raw_v6_hash(struct sock *sk){ struct sock **skp = &raw_v6_htable[sk->num & (RAWV6_HTABLE_SIZE - 1)]; write_lock_bh(&raw_v6_lock); if ((sk->next = *skp) != NULL) (*skp)->pprev = &sk->next; *skp = sk; sk->pprev = skp; sock_prot_inc_use(sk->prot); sock_hold(sk); write_unlock_bh(&raw_v6_lock);}static void raw_v6_unhash(struct sock *sk){ write_lock_bh(&raw_v6_lock); if (sk->pprev) { if (sk->next) sk->next->pprev = sk->pprev; *sk->pprev = sk->next; sk->pprev = NULL; sock_prot_dec_use(sk->prot); __sock_put(sk); } write_unlock_bh(&raw_v6_lock);}/* Grumble... icmp and ip_input want to get at this... */struct sock *__raw_v6_lookup(struct sock *sk, unsigned short num, struct in6_addr *loc_addr, struct in6_addr *rmt_addr){ struct sock *s = sk; int addr_type = ipv6_addr_type(loc_addr); for(s = sk; s; s = s->next) { if(s->num == num) { struct ipv6_pinfo *np = &s->net_pinfo.af_inet6; if (!ipv6_addr_any(&np->daddr) && ipv6_addr_cmp(&np->daddr, rmt_addr)) continue; if (!ipv6_addr_any(&np->rcv_saddr)) { if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0) break; if ((addr_type & IPV6_ADDR_MULTICAST) && inet6_mc_check(s, loc_addr)) break; continue; } break; } } return s;}/* * 0 - deliver * 1 - block */static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb){ struct icmp6hdr *icmph; struct raw6_opt *opt; opt = &sk->tp_pinfo.tp_raw; if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) { __u32 *data = &opt->filter.data[0]; int bit_nr; icmph = (struct icmp6hdr *) skb->data; bit_nr = icmph->icmp6_type; return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0; } return 0;}/* * demultiplex raw sockets. * (should consider queueing the skb in the sock receive_queue * without calling rawv6.c) */struct sock * ipv6_raw_deliver(struct sk_buff *skb, int nexthdr){ struct in6_addr *saddr; struct in6_addr *daddr; struct sock *sk, *sk2; __u8 hash; saddr = &skb->nh.ipv6h->saddr; daddr = saddr + 1; hash = nexthdr & (MAX_INET_PROTOS - 1); read_lock(&raw_v6_lock); sk = raw_v6_htable[hash]; /* * The first socket found will be delivered after * delivery to transport protocols. */ if (sk == NULL) goto out; sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr); if (sk) { sk2 = sk; while ((sk2 = __raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) { struct sk_buff *buff; if (nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk2, skb)) continue; buff = skb_clone(skb, GFP_ATOMIC); if (buff) rawv6_rcv(sk2, buff); } } if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb)) sk = NULL;out: if (sk) sock_hold(sk); read_unlock(&raw_v6_lock); return sk;}/* This cleans up af_inet6 a bit. -DaveM */static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len){ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr; __u32 v4addr = 0; int addr_type; int err; if (addr_len < SIN6_LEN_RFC2133) return -EINVAL; addr_type = ipv6_addr_type(&addr->sin6_addr); /* Raw sockets are IPv6 only */ if (addr_type == IPV6_ADDR_MAPPED) return(-EADDRNOTAVAIL); lock_sock(sk); err = -EINVAL; if (sk->state != TCP_CLOSE) goto out; 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) goto out; } /* Check if the address belongs to the host. */ 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)) { err = -EADDRNOTAVAIL; if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) goto out; } } 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); err = 0;out: release_sock(sk); return err;}void rawv6_err(struct sock *sk, struct sk_buff *skb, struct inet6_skb_parm *opt, int type, int code, int offset, u32 info){ int err; int harderr; /* Report error on raw socket, if: 1. User requested recverr. 2. Socket is connected (otherwise the error indication is useless without recverr and error is hard. */ if (!sk->net_pinfo.af_inet6.recverr && sk->state != TCP_ESTABLISHED) return; harderr = icmpv6_err_convert(type, code, &err); if (type == ICMPV6_PKT_TOOBIG) harderr = (sk->net_pinfo.af_inet6.pmtudisc == IPV6_PMTUDISC_DO); if (sk->net_pinfo.af_inet6.recverr) { u8 *payload = skb->data; if (!sk->protinfo.af_inet.hdrincl) payload += offset; ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload); } if (sk->net_pinfo.af_inet6.recverr || harderr) { sk->err = err; sk->error_report(sk); }}static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb){ /* Charge it to the socket. */ if (sock_queue_rcv_skb(sk,skb)<0) { IP6_INC_STATS_BH(Ip6InDiscards); kfree_skb(skb); return 0; } IP6_INC_STATS_BH(Ip6InDelivers); return 0;}/* * This is next to useless... * if we demultiplex in network layer we don't need the extra call * just to queue the skb... * maybe we could have the network decide uppon a hint if it * should call raw_rcv for demultiplexing */int rawv6_rcv(struct sock *sk, struct sk_buff *skb){ if (sk->protinfo.af_inet.hdrincl) { __skb_push(skb, skb->nh.raw - skb->data); skb->h.raw = skb->nh.raw; } rawv6_rcv_skb(sk, skb); return 0;}/* * This should be easy, if there is something there * we return it, otherwise we block. */int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, int flags, int *addr_len){ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)msg->msg_name; struct sk_buff *skb; int copied, err; if (flags & MSG_OOB) return -EOPNOTSUPP; if (addr_len) *addr_len=sizeof(*sin6); if (flags & MSG_ERRQUEUE) return ipv6_recv_error(sk, msg, len); skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) goto out; copied = skb->len; if (copied > len) { copied = len; msg->msg_flags |= MSG_TRUNC; } err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (err) goto out_free; /* Copy the address. */ if (sin6) { sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, sizeof(struct in6_addr)); sin6->sin6_flowinfo = 0; sin6->sin6_scope_id = 0; if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; sin6->sin6_scope_id = opt->iif; } } sock_recv_timestamp(msg, sk, skb); if (sk->net_pinfo.af_inet6.rxopt.all) datagram_recv_ctl(sk, msg, skb); err = copied;out_free: skb_free_datagram(sk, skb);out: return err;}/* * Sending... */struct rawv6_fakehdr { struct iovec *iov; struct sock *sk; __u32 len; __u32 cksum; __u32 proto; struct in6_addr *daddr;};static int rawv6_getfrag(const void *data, struct in6_addr *saddr, char *buff, unsigned int offset, unsigned int len){ struct iovec *iov = (struct iovec *) data; return memcpy_fromiovecend(buff, iov, offset, len);}static int rawv6_frag_cksum(const void *data, struct in6_addr *addr, char *buff, unsigned int offset, unsigned int len){ struct rawv6_fakehdr *hdr = (struct rawv6_fakehdr *) data; if (csum_partial_copy_fromiovecend(buff, hdr->iov, offset, len, &hdr->cksum)) return -EFAULT; if (offset == 0) { struct sock *sk; struct raw6_opt *opt; struct in6_addr *daddr; sk = hdr->sk; opt = &sk->tp_pinfo.tp_raw; if (hdr->daddr) daddr = hdr->daddr; else daddr = addr + 1; hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len, hdr->proto, hdr->cksum); if (opt->offset < len) { __u16 *csum;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -