/* We require only the four bytes of the ICMPv6 header, not any * additional bytes of message body in "struct icmp6hdr".
*/
hdr = skb_header_pointer(skb, skb_transport_offset(skb),
ICMPV6_HDRLEN, &_hdr); if (hdr) { const __u32 *data = &raw6_sk(sk)->filter.data[0]; unsignedint type = hdr->icmp6_type;
#if IS_ENABLED(CONFIG_IPV6_MIP6) case IPPROTO_MH:
{ /* XXX: To validate MH only once for each packet, * this is placed here. It should be after checking * xfrm policy, however it doesn't. The checking xfrm * policy is placed in rawv6_rcv() because it is * required for each socket.
*/
mh_filter_t *filter;
/* This cleans up af_inet6 a bit. -DaveM */ staticint rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ struct inet_sock *inet = inet_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
__be32 v4addr = 0; int addr_type; int err;
if (addr_len < SIN6_LEN_RFC2133) return -EINVAL;
if (addr->sin6_family != AF_INET6) 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->sk_state != TCP_CLOSE) goto out;
rcu_read_lock(); /* Check if the address belongs to the host. */ if (addr_type != IPV6_ADDR_ANY) { struct net_device *dev = NULL;
if (__ipv6_addr_needs_scope_id(addr_type)) { 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) goto out_unlock;
}
if (sk->sk_bound_dev_if) {
err = -ENODEV;
dev = dev_get_by_index_rcu(sock_net(sk),
sk->sk_bound_dev_if); if (!dev) goto out_unlock;
}
/* 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) &&
!ipv6_can_nonlocal_bind(sock_net(sk), inet)) {
err = -EADDRNOTAVAIL; if (!ipv6_chk_addr(sock_net(sk), &addr->sin6_addr,
dev, 0)) { goto out_unlock;
}
}
}
staticvoid rawv6_err(struct sock *sk, struct sk_buff *skb,
u8 type, u8 code, int offset, __be32 info)
{ bool recverr = inet6_test_bit(RECVERR6, sk); struct ipv6_pinfo *np = inet6_sk(sk); 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 (!recverr && sk->sk_state != TCP_ESTABLISHED) return;
/* Charge it to the socket. */
skb_dst_drop(skb); if (sock_queue_rcv_skb_reason(sk, skb, &reason) < 0) {
sk_skb_reason_drop(sk, skb, reason); return NET_RX_DROP;
}
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 upon a hint if it * should call raw_rcv for demultiplexing
*/ int rawv6_rcv(struct sock *sk, struct sk_buff *skb)
{ struct inet_sock *inet = inet_sk(sk); struct raw6_sock *rp = raw6_sk(sk);
/* should be check HW csum miyazawa */ if (skb_queue_len(&sk->sk_write_queue) == 1) { /* * Only one fragment on the socket.
*/
tmp_csum = skb->csum;
} else { struct sk_buff *csum_skb = NULL;
tmp_csum = 0;
/* if egress device is enslaved to an L3 master device pass the * skb to its handler for processing
*/
skb = l3mdev_ip6_out(sk, skb); if (unlikely(!skb)) return 0;
/* Acquire rcu_read_lock() in case we need to use rt->rt6i_idev * in the error path. Since skb has been freed, the dst could * have been queued for deletion.
*/
rcu_read_lock();
IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS);
err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, net, sk, skb,
NULL, rt->dst.dev, dst_output); if (err > 0)
err = net_xmit_errno(err); if (err) {
IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
rcu_read_unlock(); goto error_check;
}
rcu_read_unlock();
out: return 0;
if (sin6) { if (addr_len < SIN6_LEN_RFC2133) return -EINVAL;
if (sin6->sin6_family && sin6->sin6_family != AF_INET6) return -EAFNOSUPPORT;
/* port is the proto value [0..255] carried in nexthdr */
proto = ntohs(sin6->sin6_port);
if (!proto)
proto = inet->inet_num; elseif (proto != inet->inet_num &&
inet->inet_num != IPPROTO_RAW) return -EINVAL;
if (proto > 255) return -EINVAL;
daddr = &sin6->sin6_addr; if (inet6_test_bit(SNDFLOW, sk)) {
fl6.flowlabel = sin6->sin6_flowinfo&IPV6_FLOWINFO_MASK; if (fl6.flowlabel&IPV6_FLOWLABEL_MASK) {
flowlabel = fl6_sock_lookup(sk, fl6.flowlabel); if (IS_ERR(flowlabel)) return -EINVAL;
}
}
/* * Otherwise it will be difficult to maintain * sk->sk_dst_cache.
*/ if (sk->sk_state == TCP_ESTABLISHED &&
ipv6_addr_equal(daddr, &sk->sk_v6_daddr))
daddr = &sk->sk_v6_daddr;
staticint rawv6_seticmpfilter(struct sock *sk, int optname,
sockptr_t optval, int optlen)
{ switch (optname) { case ICMPV6_FILTER: if (optlen > sizeof(struct icmp6_filter))
optlen = sizeof(struct icmp6_filter); if (copy_from_sockptr(&raw6_sk(sk)->filter, optval, optlen)) return -EFAULT; return 0; default: return -ENOPROTOOPT;
}
return 0;
}
staticint rawv6_geticmpfilter(struct sock *sk, int optname, char __user *optval, int __user *optlen)
{ int len;
switch (optname) { case ICMPV6_FILTER: if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; if (len > sizeof(struct icmp6_filter))
len = sizeof(struct icmp6_filter); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &raw6_sk(sk)->filter, len)) return -EFAULT; return 0; default: return -ENOPROTOOPT;
}
return 0;
}
staticint do_rawv6_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct raw6_sock *rp = raw6_sk(sk); int val;
if (optlen < sizeof(val)) return -EINVAL;
if (copy_from_sockptr(&val, optval, sizeof(val))) return -EFAULT;
switch (optname) { case IPV6_HDRINCL: if (sk->sk_type != SOCK_RAW) return -EINVAL;
inet_assign_bit(HDRINCL, sk, val); return 0; case IPV6_CHECKSUM: if (inet_sk(sk)->inet_num == IPPROTO_ICMPV6 &&
level == IPPROTO_IPV6) { /* * RFC3542 tells that IPV6_CHECKSUM socket * option in the IPPROTO_IPV6 level is not * allowed on ICMPv6 sockets. * If you want to set it, use IPPROTO_RAW * level IPV6_CHECKSUM socket option * (Linux extension).
*/ return -EINVAL;
}
/* You may get strange result with a positive odd offset;
RFC2292bis agrees with me. */ if (val > 0 && (val&1)) return -EINVAL; if (val < 0) {
rp->checksum = 0;
} else {
rp->checksum = 1;
rp->offset = val;
}
return 0;
default: return -ENOPROTOOPT;
}
}
staticint rawv6_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ switch (level) { case SOL_RAW: break;
case SOL_ICMPV6: if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) return -EOPNOTSUPP; return rawv6_seticmpfilter(sk, optname, optval, optlen); case SOL_IPV6: if (optname == IPV6_CHECKSUM ||
optname == IPV6_HDRINCL) break;
fallthrough; default: return ipv6_setsockopt(sk, level, optname, optval, optlen);
}
staticint do_rawv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen)
{ struct raw6_sock *rp = raw6_sk(sk); int val, len;
if (get_user(len, optlen)) return -EFAULT;
switch (optname) { case IPV6_HDRINCL:
val = inet_test_bit(HDRINCL, sk); break; case IPV6_CHECKSUM: /* * We allow getsockopt() for IPPROTO_IPV6-level * IPV6_CHECKSUM socket option on ICMPv6 sockets * since RFC3542 is silent about it.
*/ if (rp->checksum == 0)
val = -1; else
val = rp->offset; break;
default: return -ENOPROTOOPT;
}
len = min_t(unsignedint, sizeof(int), len);
if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; return 0;
}
staticint rawv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen)
{ switch (level) { case SOL_RAW: break;
case SOL_ICMPV6: if (inet_sk(sk)->inet_num != IPPROTO_ICMPV6) return -EOPNOTSUPP; return rawv6_geticmpfilter(sk, optname, optval, optlen); case SOL_IPV6: if (optname == IPV6_CHECKSUM ||
optname == IPV6_HDRINCL) break;
fallthrough; default: return ipv6_getsockopt(sk, level, optname, optval, optlen);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.