static void dn_nsp_send(struct sk_buff *skb) { struct sock *sk = skb->sk; struct dn_scp *scp = DN_SK(sk); struct dst_entry *dst; struct flowi fl; skb_reset_transport_header(skb); scp->stamp = jiffies; dst = sk_dst_check(sk, 0); if (dst) { try_again: skb_dst_set(skb, dst); dst_output(skb); return; } memset(&fl, 0, sizeof(fl)); fl.oif = sk->sk_bound_dev_if; fl.fld_src = dn_saddr2dn(&scp->addr); fl.fld_dst = dn_saddr2dn(&scp->peer); dn_sk_ports_copy(&fl, scp); fl.proto = DNPROTO_NSP; if (dn_route_output_sock(&sk->sk_dst_cache, &fl, sk, 0) == 0) { dst = sk_dst_get(sk); sk->sk_route_caps = dst->dev->features; goto try_again; } sk->sk_err = EHOSTUNREACH; if (!sock_flag(sk, SOCK_DEAD)) sk->sk_state_change(sk); }
struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie) { struct dst_entry *dst = sk_dst_get(sk); if (dst && dst->obsolete && dst->ops->check(dst, cookie) == NULL) { sk_dst_reset(sk); dst_release(dst); return NULL; } return dst; }
static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, struct l2tp_session *session, struct net_device *dev) { unsigned int overhead = 0; struct dst_entry *dst; u32 l3_overhead = 0; /* if the encap is UDP, account for UDP header size */ if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { overhead += sizeof(struct udphdr); dev->needed_headroom += sizeof(struct udphdr); } if (session->mtu != 0) { dev->mtu = session->mtu; dev->needed_headroom += session->hdr_len; return; } lock_sock(tunnel->sock); l3_overhead = kernel_sock_ip_overhead(tunnel->sock); release_sock(tunnel->sock); if (l3_overhead == 0) { /* L3 Overhead couldn't be identified, this could be * because tunnel->sock was NULL or the socket's * address family was not IPv4 or IPv6, * dev mtu stays at 1500. */ return; } /* Adjust MTU, factor overhead - underlay L3, overlay L2 hdr * UDP overhead, if any, was already factored in above. */ overhead += session->hdr_len + ETH_HLEN + l3_overhead; /* If PMTU discovery was enabled, use discovered MTU on L2TP device */ dst = sk_dst_get(tunnel->sock); if (dst) { /* dst_mtu will use PMTU if found, else fallback to intf MTU */ u32 pmtu = dst_mtu(dst); if (pmtu != 0) dev->mtu = pmtu; dst_release(dst); } session->mtu = dev->mtu - overhead; dev->mtu = session->mtu; dev->needed_headroom += session->hdr_len; }
static struct dst_entry *rxe_find_route(struct net_device *ndev, struct rxe_qp *qp, struct rxe_av *av) { struct dst_entry *dst = NULL; if (qp_type(qp) == IB_QPT_RC) dst = sk_dst_get(qp->sk->sk); if (!dst || !dst_check(dst, qp->dst_cookie)) { if (dst) dst_release(dst); if (av->network_type == RDMA_NETWORK_IPV4) { struct in_addr *saddr; struct in_addr *daddr; saddr = &av->sgid_addr._sockaddr_in.sin_addr; daddr = &av->dgid_addr._sockaddr_in.sin_addr; dst = rxe_find_route4(ndev, saddr, daddr); } else if (av->network_type == RDMA_NETWORK_IPV6) { struct in6_addr *saddr6; struct in6_addr *daddr6; saddr6 = &av->sgid_addr._sockaddr_in6.sin6_addr; daddr6 = &av->dgid_addr._sockaddr_in6.sin6_addr; dst = rxe_find_route6(ndev, saddr6, daddr6); #if IS_ENABLED(CONFIG_IPV6) if (dst) qp->dst_cookie = rt6_get_cookie((struct rt6_info *)dst); #endif } if (dst && (qp_type(qp) == IB_QPT_RC)) { dst_hold(dst); sk_dst_set(qp->sk->sk, dst); } } return dst; }
int ipv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { struct ipv6_pinfo *np = inet6_sk(sk); int len; int val; if (level == SOL_IP && sk->sk_type != SOCK_RAW) return udp_prot.getsockopt(sk, level, optname, optval, optlen); if(level!=SOL_IPV6) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; switch (optname) { case IPV6_ADDRFORM: if (sk->sk_protocol != IPPROTO_UDP && sk->sk_protocol != IPPROTO_TCP) return -EINVAL; if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; val = sk->sk_family; break; case MCAST_MSFILTER: { struct group_filter gsf; int err; if (len < GROUP_FILTER_SIZE(0)) return -EINVAL; if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) return -EFAULT; lock_sock(sk); err = ip6_mc_msfget(sk, &gsf, (struct group_filter __user *)optval, optlen); release_sock(sk); return err; } case IPV6_PKTOPTIONS: { struct msghdr msg; struct sk_buff *skb; if (sk->sk_type != SOCK_STREAM) return -ENOPROTOOPT; msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; lock_sock(sk); skb = np->pktoptions; if (skb) atomic_inc(&skb->users); release_sock(sk); if (skb) { int err = datagram_recv_ctl(sk, &msg, skb); kfree_skb(skb); if (err) return err; } else { if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif; ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr); put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { int hlim = np->mcast_hops; put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } } len -= msg.msg_controllen; return put_user(len, optlen); } case IPV6_MTU: { struct dst_entry *dst; val = 0; lock_sock(sk); dst = sk_dst_get(sk); if (dst) { val = dst_mtu(dst); dst_release(dst); } release_sock(sk); if (!val) return -ENOTCONN; break; } case IPV6_V6ONLY: val = np->ipv6only; break; case IPV6_PKTINFO: val = np->rxopt.bits.rxinfo; break; case IPV6_HOPLIMIT: val = np->rxopt.bits.rxhlim; break; case IPV6_RTHDR: val = np->rxopt.bits.srcrt; break; case IPV6_HOPOPTS: val = np->rxopt.bits.hopopts; break; case IPV6_DSTOPTS: val = np->rxopt.bits.dstopts; break; case IPV6_FLOWINFO: val = np->rxopt.bits.rxflow; break; case IPV6_UNICAST_HOPS: val = np->hop_limit; break; case IPV6_MULTICAST_HOPS: val = np->mcast_hops; break; case IPV6_MULTICAST_LOOP: val = np->mc_loop; break; case IPV6_MULTICAST_IF: val = np->mcast_oif; break; case IPV6_MTU_DISCOVER: val = np->pmtudisc; break; case IPV6_RECVERR: val = np->recverr; break; case IPV6_FLOWINFO_SEND: val = np->sndflow; break; default: #ifdef CONFIG_NETFILTER lock_sock(sk); val = nf_getsockopt(sk, PF_INET6, optname, optval, &len); release_sock(sk); if (val >= 0) val = put_user(len, optlen); return val; #else return -EINVAL; #endif } len = min_t(unsigned int, sizeof(int), len); if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval,&val,len)) return -EFAULT; return 0; }
static int do_ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen, unsigned int flags) { struct inet_sock *inet = inet_sk(sk); bool needs_rtnl = getsockopt_needs_rtnl(optname); int val, err = 0; int len; if (level != SOL_IP) return -EOPNOTSUPP; if (ip_mroute_opt(optname)) return ip_mroute_getsockopt(sk, optname, optval, optlen); if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; if (needs_rtnl) rtnl_lock(); lock_sock(sk); switch (optname) { case IP_OPTIONS: { unsigned char optbuf[sizeof(struct ip_options)+40]; struct ip_options *opt = (struct ip_options *)optbuf; struct ip_options_rcu *inet_opt; inet_opt = rcu_dereference_protected(inet->inet_opt, lockdep_sock_is_held(sk)); opt->optlen = 0; if (inet_opt) memcpy(optbuf, &inet_opt->opt, sizeof(struct ip_options) + inet_opt->opt.optlen); release_sock(sk); if (opt->optlen == 0) return put_user(0, optlen); ip_options_undo(opt); len = min_t(unsigned int, len, opt->optlen); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, opt->__data, len)) return -EFAULT; return 0; } case IP_PKTINFO: val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; break; case IP_RECVTTL: val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; break; case IP_RECVTOS: val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; break; case IP_RECVOPTS: val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; break; case IP_RETOPTS: val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; break; case IP_PASSSEC: val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; break; case IP_RECVORIGDSTADDR: val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; break; case IP_CHECKSUM: val = (inet->cmsg_flags & IP_CMSG_CHECKSUM) != 0; break; case IP_RECVFRAGSIZE: val = (inet->cmsg_flags & IP_CMSG_RECVFRAGSIZE) != 0; break; case IP_TOS: val = inet->tos; break; case IP_TTL: { struct net *net = sock_net(sk); val = (inet->uc_ttl == -1 ? net->ipv4.sysctl_ip_default_ttl : inet->uc_ttl); break; } case IP_HDRINCL: val = inet->hdrincl; break; case IP_NODEFRAG: val = inet->nodefrag; break; case IP_BIND_ADDRESS_NO_PORT: val = inet->bind_address_no_port; break; case IP_MTU_DISCOVER: val = inet->pmtudisc; break; case IP_MTU: { struct dst_entry *dst; val = 0; dst = sk_dst_get(sk); if (dst) { val = dst_mtu(dst); dst_release(dst); } if (!val) { release_sock(sk); return -ENOTCONN; } break; } case IP_RECVERR: val = inet->recverr; break; case IP_MULTICAST_TTL: val = inet->mc_ttl; break; case IP_MULTICAST_LOOP: val = inet->mc_loop; break; case IP_UNICAST_IF: val = (__force int)htonl((__u32) inet->uc_index); break; case IP_MULTICAST_IF: { struct in_addr addr; len = min_t(unsigned int, len, sizeof(struct in_addr)); addr.s_addr = inet->mc_addr; release_sock(sk); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &addr, len)) return -EFAULT; return 0; } case IP_MSFILTER: { struct ip_msfilter msf; if (len < IP_MSFILTER_SIZE(0)) { err = -EINVAL; goto out; } if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { err = -EFAULT; goto out; } err = ip_mc_msfget(sk, &msf, (struct ip_msfilter __user *)optval, optlen); goto out; } case MCAST_MSFILTER: { struct group_filter gsf; if (len < GROUP_FILTER_SIZE(0)) { err = -EINVAL; goto out; } if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { err = -EFAULT; goto out; } err = ip_mc_gsfget(sk, &gsf, (struct group_filter __user *)optval, optlen); goto out; } case IP_MULTICAST_ALL: val = inet->mc_all; break; case IP_PKTOPTIONS: { struct msghdr msg; release_sock(sk); if (sk->sk_type != SOCK_STREAM) return -ENOPROTOOPT; msg.msg_control = (__force void *) optval; msg.msg_controllen = len; msg.msg_flags = flags; if (inet->cmsg_flags & IP_CMSG_PKTINFO) { struct in_pktinfo info; info.ipi_addr.s_addr = inet->inet_rcv_saddr; info.ipi_spec_dst.s_addr = inet->inet_rcv_saddr; info.ipi_ifindex = inet->mc_index; put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); } if (inet->cmsg_flags & IP_CMSG_TTL) { int hlim = inet->mc_ttl; put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); } if (inet->cmsg_flags & IP_CMSG_TOS) { int tos = inet->rcv_tos; put_cmsg(&msg, SOL_IP, IP_TOS, sizeof(tos), &tos); } len -= msg.msg_controllen; return put_user(len, optlen); } case IP_FREEBIND: val = inet->freebind; break; case IP_TRANSPARENT: val = inet->transparent; break; case IP_MINTTL: val = inet->min_ttl; break; default: release_sock(sk); return -ENOPROTOOPT; } release_sock(sk); if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { unsigned char ucval = (unsigned char)val; len = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &ucval, 1)) return -EFAULT; } else { len = min_t(unsigned int, sizeof(int), len); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; } return 0; out: release_sock(sk); if (needs_rtnl) rtnl_unlock(); return err; }
static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { struct ipv6_pinfo *np = inet6_sk(sk); int len; int val; if (ip6_mroute_opt(optname)) return ip6_mroute_getsockopt(sk, optname, optval, optlen); if (get_user(len, optlen)) return -EFAULT; switch (optname) { case IPV6_ADDRFORM: if (sk->sk_protocol != IPPROTO_UDP && sk->sk_protocol != IPPROTO_UDPLITE && sk->sk_protocol != IPPROTO_TCP) return -ENOPROTOOPT; if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; val = sk->sk_family; break; case MCAST_MSFILTER: { struct group_filter gsf; int err; if (len < GROUP_FILTER_SIZE(0)) return -EINVAL; if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) return -EFAULT; if (gsf.gf_group.ss_family != AF_INET6) return -EADDRNOTAVAIL; lock_sock(sk); err = ip6_mc_msfget(sk, &gsf, (struct group_filter __user *)optval, optlen); release_sock(sk); return err; } case IPV6_2292PKTOPTIONS: { struct msghdr msg; struct sk_buff *skb; if (sk->sk_type != SOCK_STREAM) return -ENOPROTOOPT; msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; lock_sock(sk); skb = np->pktoptions; if (skb) atomic_inc(&skb->users); release_sock(sk); if (skb) { int err = datagram_recv_ctl(sk, &msg, skb); kfree_skb(skb); if (err) return err; } else { if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : np->sticky_pktinfo.ipi6_ifindex; np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) : ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr)); put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { int hlim = np->mcast_hops; put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } if (np->rxopt.bits.rxoinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : np->sticky_pktinfo.ipi6_ifindex; np->mcast_oif? ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr) : ipv6_addr_copy(&src_info.ipi6_addr, &(np->sticky_pktinfo.ipi6_addr)); put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxohlim) { int hlim = np->mcast_hops; put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim); } } len -= msg.msg_controllen; return put_user(len, optlen); } case IPV6_MTU: { struct dst_entry *dst; val = 0; lock_sock(sk); dst = sk_dst_get(sk); if (dst) { val = dst_mtu(dst); dst_release(dst); } release_sock(sk); if (!val) return -ENOTCONN; break; } case IPV6_V6ONLY: val = np->ipv6only; break; case IPV6_RECVPKTINFO: val = np->rxopt.bits.rxinfo; break; case IPV6_2292PKTINFO: val = np->rxopt.bits.rxoinfo; break; case IPV6_RECVHOPLIMIT: val = np->rxopt.bits.rxhlim; break; case IPV6_2292HOPLIMIT: val = np->rxopt.bits.rxohlim; break; case IPV6_RECVRTHDR: val = np->rxopt.bits.srcrt; break; case IPV6_2292RTHDR: val = np->rxopt.bits.osrcrt; break; case IPV6_HOPOPTS: case IPV6_RTHDRDSTOPTS: case IPV6_RTHDR: case IPV6_DSTOPTS: { lock_sock(sk); len = ipv6_getsockopt_sticky(sk, np->opt, optname, optval, len); release_sock(sk); /* check if ipv6_getsockopt_sticky() returns err code */ if (len < 0) return len; return put_user(len, optlen); } case IPV6_RECVHOPOPTS: val = np->rxopt.bits.hopopts; break; case IPV6_2292HOPOPTS: val = np->rxopt.bits.ohopopts; break; case IPV6_RECVDSTOPTS: val = np->rxopt.bits.dstopts; break; case IPV6_2292DSTOPTS: val = np->rxopt.bits.odstopts; break; case IPV6_TCLASS: val = np->tclass; if (val < 0) val = 0; break; case IPV6_RECVTCLASS: val = np->rxopt.bits.rxtclass; break; case IPV6_FLOWINFO: val = np->rxopt.bits.rxflow; break; case IPV6_UNICAST_HOPS: case IPV6_MULTICAST_HOPS: { struct dst_entry *dst; if (optname == IPV6_UNICAST_HOPS) val = np->hop_limit; else val = np->mcast_hops; dst = sk_dst_get(sk); if (dst) { if (val < 0) val = ip6_dst_hoplimit(dst); dst_release(dst); } if (val < 0) val = sock_net(sk)->ipv6.devconf_all->hop_limit; break; } case IPV6_MULTICAST_LOOP: val = np->mc_loop; break; case IPV6_MULTICAST_IF: val = np->mcast_oif; break; case IPV6_MTU_DISCOVER: val = np->pmtudisc; break; case IPV6_RECVERR: val = np->recverr; break; case IPV6_FLOWINFO_SEND: val = np->sndflow; break; case IPV6_ADDR_PREFERENCES: val = 0; if (np->srcprefs & IPV6_PREFER_SRC_TMP) val |= IPV6_PREFER_SRC_TMP; else if (np->srcprefs & IPV6_PREFER_SRC_PUBLIC) val |= IPV6_PREFER_SRC_PUBLIC; else { /* XXX: should we return system default? */ val |= IPV6_PREFER_SRC_PUBTMP_DEFAULT; } if (np->srcprefs & IPV6_PREFER_SRC_COA) val |= IPV6_PREFER_SRC_COA; else val |= IPV6_PREFER_SRC_HOME; break; default: return -ENOPROTOOPT; } len = min_t(unsigned int, sizeof(int), len); if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval,&val,len)) return -EFAULT; return 0; }
int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen) { struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; int len; int val; if(level==SOL_IP && sk->type != SOCK_RAW) return udp_prot.getsockopt(sk, level, optname, optval, optlen); if(level!=SOL_IPV6) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; if (optlen < 0) return -EINVAL; switch (optname) { case IPV6_PKTOPTIONS: { struct msghdr msg; struct sk_buff *skb; if (sk->type != SOCK_STREAM) return -ENOPROTOOPT; msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; lock_sock(sk); skb = np->pktoptions; if (skb) atomic_inc(&skb->users); release_sock(sk); if (skb) { int err = datagram_recv_ctl(sk, &msg, skb); kfree_skb(skb); if (err) return err; } else { if (np->rxopt.bits.rxinfo) { struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif; ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr); put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { int hlim = np->mcast_hops; put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); } } len -= msg.msg_controllen; return put_user(len, optlen); } case IPV6_MTU: { struct dst_entry *dst; val = 0; lock_sock(sk); dst = sk_dst_get(sk); if (dst) { val = dst->pmtu; dst_release(dst); } release_sock(sk); if (!val) return -ENOTCONN; break; } case IPV6_PKTINFO: val = np->rxopt.bits.rxinfo; break; case IPV6_HOPLIMIT: val = np->rxopt.bits.rxhlim; break; case IPV6_RTHDR: val = np->rxopt.bits.srcrt; break; case IPV6_HOPOPTS: val = np->rxopt.bits.hopopts; break; case IPV6_AUTHHDR: val = np->rxopt.bits.authhdr; break; case IPV6_DSTOPTS: val = np->rxopt.bits.dstopts; break; case IPV6_FLOWINFO: val = np->rxopt.bits.rxflow; break; case IPV6_UNICAST_HOPS: val = np->hop_limit; break; case IPV6_MULTICAST_HOPS: val = np->mcast_hops; break; case IPV6_MULTICAST_LOOP: val = np->mc_loop; break; case IPV6_MULTICAST_IF: val = np->mcast_oif; break; case IPV6_MTU_DISCOVER: val = np->pmtudisc; break; case IPV6_RECVERR: val = np->recverr; break; case IPV6_FLOWINFO_SEND: val = np->sndflow; break; case IPV6_JOIN_GROUP: case IPV6_LEAVE_GROUP: return -EOPNOTSUPP; case IPV6_V6ONLY: val = np->ipv6only; break; case IPV6_RECVTCLASS: val = np->recvopt.tclass; break; case IPV6_TCLASS: val = np->tclass; break; default: #ifdef CONFIG_NETFILTER lock_sock(sk); val = nf_getsockopt(sk, PF_INET6, optname, optval, &len); release_sock(sk); if (val >= 0) val = put_user(len, optlen); return val; #else return -EINVAL; #endif } len = min_t(unsigned int, sizeof(int), len); if(put_user(len, optlen)) return -EFAULT; if(copy_to_user(optval,&val,len)) return -EFAULT; return 0; }
static int ax8netfilter_do_ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { struct inet_sock *inet = inet_sk(sk); int val; int len; if (level != SOL_IP) return -EOPNOTSUPP; if (ip_mroute_opt(optname)) return ip_mroute_getsockopt(sk, optname, optval, optlen); if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; lock_sock(sk); switch (optname) { case IP_OPTIONS: { unsigned char optbuf[sizeof(struct ip_options)+40]; struct ip_options * opt = (struct ip_options *)optbuf; opt->optlen = 0; if (inet->opt) memcpy(optbuf, inet->opt, sizeof(struct ip_options)+ inet->opt->optlen); release_sock(sk); if (opt->optlen == 0) return put_user(0, optlen); ax8netfilter_ip_options_undo(opt); len = min_t(unsigned int, len, opt->optlen); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, opt->__data, len)) return -EFAULT; return 0; } case IP_PKTINFO: val = (inet->cmsg_flags & IP_CMSG_PKTINFO) != 0; break; case IP_RECVTTL: val = (inet->cmsg_flags & IP_CMSG_TTL) != 0; break; case IP_RECVTOS: val = (inet->cmsg_flags & IP_CMSG_TOS) != 0; break; case IP_RECVOPTS: val = (inet->cmsg_flags & IP_CMSG_RECVOPTS) != 0; break; case IP_RETOPTS: val = (inet->cmsg_flags & IP_CMSG_RETOPTS) != 0; break; case IP_PASSSEC: val = (inet->cmsg_flags & IP_CMSG_PASSSEC) != 0; break; case IP_RECVORIGDSTADDR: val = (inet->cmsg_flags & IP_CMSG_ORIGDSTADDR) != 0; break; case IP_TOS: val = inet->tos; break; case IP_TTL: val = (inet->uc_ttl == -1 ? *ax8netfilter_sysctl_ip_default_ttl : inet->uc_ttl); break; case IP_HDRINCL: val = inet->hdrincl; break; case IP_MTU_DISCOVER: val = inet->pmtudisc; break; case IP_MTU: { struct dst_entry *dst; val = 0; dst = sk_dst_get(sk); if (dst) { val = dst_mtu(dst); dst_release(dst); } if (!val) { release_sock(sk); return -ENOTCONN; } break; } case IP_RECVERR: val = inet->recverr; break; case IP_MULTICAST_TTL: val = inet->mc_ttl; break; case IP_MULTICAST_LOOP: val = inet->mc_loop; break; case IP_MULTICAST_IF: { struct in_addr addr; len = min_t(unsigned int, len, sizeof(struct in_addr)); addr.s_addr = inet->mc_addr; release_sock(sk); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &addr, len)) return -EFAULT; return 0; } case IP_MSFILTER: { struct ip_msfilter msf; int err; if (len < IP_MSFILTER_SIZE(0)) { release_sock(sk); return -EINVAL; } if (copy_from_user(&msf, optval, IP_MSFILTER_SIZE(0))) { release_sock(sk); return -EFAULT; } err = ax8netfilter_ip_mc_msfget(sk, &msf, (struct ip_msfilter __user *)optval, optlen); release_sock(sk); return err; } case MCAST_MSFILTER: { struct group_filter gsf; int err; if (len < GROUP_FILTER_SIZE(0)) { release_sock(sk); return -EINVAL; } if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0))) { release_sock(sk); return -EFAULT; } err = ax8netfilter_ip_mc_gsfget(sk, &gsf, (struct group_filter __user *)optval, optlen); release_sock(sk); return err; } case IP_PKTOPTIONS: { struct msghdr msg; release_sock(sk); if (sk->sk_type != SOCK_STREAM) return -ENOPROTOOPT; msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; if (inet->cmsg_flags & IP_CMSG_PKTINFO) { struct in_pktinfo info; info.ipi_addr.s_addr = inet->rcv_saddr; info.ipi_spec_dst.s_addr = inet->rcv_saddr; info.ipi_ifindex = inet->mc_index; put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); } if (inet->cmsg_flags & IP_CMSG_TTL) { int hlim = inet->mc_ttl; put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); } len -= msg.msg_controllen; return put_user(len, optlen); } case IP_FREEBIND: val = inet->freebind; break; case IP_TRANSPARENT: val = inet->transparent; break; default: release_sock(sk); return -ENOPROTOOPT; } release_sock(sk); if (len < sizeof(int) && len > 0 && val>=0 && val<=255) { unsigned char ucval = (unsigned char)val; len = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &ucval, 1)) return -EFAULT; } else { len = min_t(unsigned int, sizeof(int), len); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT; } return 0; }