From a72e2840e5228b8b42e7906664885cc463e0cb68 Mon Sep 17 00:00:00 2001 From: duxbbo Date: Mon, 25 Jul 2022 06:56:56 +0000 Subject: [PATCH] add files Signed-off-by: duxbbo --- code/linux/net/newip/nip_input.c | 149 ++++++++ code/linux/net/newip/nip_output.c | 510 ++++++++++++++++++++++++++++ code/linux/net/newip/nip_sockglue.c | 167 +++++++++ 3 files changed, 826 insertions(+) create mode 100644 code/linux/net/newip/nip_input.c create mode 100644 code/linux/net/newip/nip_output.c create mode 100644 code/linux/net/newip/nip_sockglue.c diff --git a/code/linux/net/newip/nip_input.c b/code/linux/net/newip/nip_input.c new file mode 100644 index 0000000..8a1120b --- /dev/null +++ b/code/linux/net/newip/nip_input.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * + * NewIP input + * Linux NewIP INET implementation + * + * Based on net/ipv6/ip6_input.c + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "nip_hdr.h" + +static int _nip_update_recv_skb_len(struct sk_buff *skb, + struct nip_hdr_decap *niph) +{ + if (!niph->include_total_len) + return 0; + + if (niph->total_len > skb->len) { + DEBUG("%s: total_len(%u) is bigger than skb_len(%u), Drop a packet.", + __func__, niph->total_len, skb->len); + return NET_RX_DROP; + } + + skb->len = niph->total_len; + return 0; +} + +static int nip_rcv_finish(struct sk_buff *skb) +{ + struct net *net = dev_net(skb->dev); + void (*edemux)(struct sk_buff *skb); + + /* set /proc/sys/net/ipv4/ip_early_demux to change sysctl_ip_early_demux, + * which is used by ipv4, ipv6 and newip + */ + if (net->ipv4.sysctl_ip_early_demux && !skb_dst(skb) && !skb->sk) { + const struct ninet_protocol *ipprot; + + DEBUG("%s: try to early demux skb.\n", __func__); + ipprot = rcu_dereference(ninet_protos[NIPCB(skb)->nexthdr]); + if (ipprot) + edemux = READ_ONCE(ipprot->early_demux); + if (edemux) + edemux(skb); + } + + /* nip_route_input will set nip_null_entry + * instead of NULL in skb when looking up failed. + */ + if (!skb_valid_dst(skb)) + nip_route_input(skb); + + return dst_input(skb); +} + +int nip_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + int offset = 0; + struct nip_hdr_decap niph = {0}; + + if (skb->pkt_type == PACKET_OTHERHOST) { + kfree_skb(skb); + return NET_RX_DROP; + } + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto out; + + memset(NIPCB(skb), 0, sizeof(struct ninet_skb_parm)); + offset = nip_hdr_parse(skb->data, skb->len, &niph); + if (offset <= 0) { + DEBUG("%s check in failure, errcode=%d, Drop a packet.(nexthdr=%u, hdr_len=%u)", + __func__, offset, niph.nexthdr, niph.hdr_len); + goto drop; + } + + if (niph.nexthdr != IPPROTO_UDP && niph.nexthdr != IPPROTO_TCP && + niph.nexthdr != IPPROTO_NIP_ICMP) { + DEBUG("%s nexthdr(%u) invalid, Drop a packet.", __func__, niph.nexthdr); + goto drop; + } + + niph.total_len = ntohs(niph.total_len); + NIPCB(skb)->dstaddr = niph.daddr; + NIPCB(skb)->srcaddr = niph.saddr; + NIPCB(skb)->nexthdr = niph.nexthdr; + skb->transport_header = skb->network_header + offset; + skb_orphan(skb); + + /* SKB refreshes the length after replication */ + if (_nip_update_recv_skb_len(skb, &niph)) + goto drop; + + return nip_rcv_finish(skb); +drop: + kfree_skb(skb); +out: + return NET_RX_DROP; +} + +/* Deliver the packet to transport layer, + * including TCP, UDP and ICMP. + * Caller must hold rcu. + */ +void nip_protocol_deliver_rcu(struct sk_buff *skb) +{ + const struct ninet_protocol *ipprot; + + if (!pskb_pull(skb, skb_transport_offset(skb))) + goto discard; + + ipprot = rcu_dereference(ninet_protos[NIPCB(skb)->nexthdr]); + if (ipprot) { + ipprot->handler(skb); + } else { + kfree_skb(skb); + DEBUG("not found transport protol, drop this packet!"); + } + return; + +discard: + kfree_skb(skb); +} + +/* Generally called by dst_input */ +int nip_input(struct sk_buff *skb) +{ + rcu_read_lock(); + nip_protocol_deliver_rcu(skb); + rcu_read_unlock(); + + return 0; +} diff --git a/code/linux/net/newip/nip_output.c b/code/linux/net/newip/nip_output.c new file mode 100644 index 0000000..ff0f041 --- /dev/null +++ b/code/linux/net/newip/nip_output.c @@ -0,0 +1,510 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * + * NewIP output functions + * Linux NewIP INET implementation + * + * Based on net/ipv6/ip6_output.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nip_hdr.h" +#include "nip_checksum.h" + +#define NIP_BIT_TO_BYTE 1024 +void update_memory_rate(const char *upper_fun) +{ + struct sysinfo mem_info; + unsigned long total; + unsigned long free; + unsigned long used; + unsigned int uint_kb; + + si_meminfo(&mem_info); + uint_kb = mem_info.mem_unit / NIP_BIT_TO_BYTE; + total = (unsigned long)mem_info.totalram * uint_kb; + free = (unsigned long)mem_info.freeram * uint_kb; + used = total - free; + DEBUG("%s -> %s mem total: %ld KB, mem used: %ld KB", upper_fun, __func__, total, used); +} + +int nip_output(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct nip_addr *nexthop; + struct neighbour *neigh; + int ret = 0; + int res; + struct net_device *dev = skb_dst(skb)->dev; + bool is_v6gw = false; + + skb->protocol = htons(ETH_P_NEWIP); + skb->dev = dev; + + /* prepare to build ethernet header */ + nexthop = nip_nexthop((struct nip_rt_info *)dst, &NIPCB(skb)->dstaddr); + + rcu_read_lock_bh(); + + neigh = __nip_neigh_lookup_noref(dev, nexthop); + if (unlikely(!neigh)) + neigh = __neigh_create(&nnd_tbl, nexthop, dev, false); + if (!IS_ERR(neigh)) { + res = neigh_output(neigh, skb, is_v6gw); + rcu_read_unlock_bh(); + return res; + } + DEBUG("find neigh and create neigh failed!"); + + rcu_read_unlock_bh(); + kfree_skb(skb); + return ret; +} + +int nip_forward(struct sk_buff *skb) +{ + return nip_output(NULL, NULL, skb); +} + +static int nip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb) +{ + int err; + + err = dst_output(net, sk, skb); + return err; +} + +int nip_send_skb(struct sk_buff *skb) +{ + struct net *net; + int err = 0; + + net = skb->sk ? sock_net(skb->sk) : dev_net(skb_dst(skb)->dev); + err = nip_local_out(net, skb->sk, skb); + if (err) + DEBUG("%s: failed to out skb!", __func__); + + return err; +} + +unsigned short nip_get_output_checksum(struct sk_buff *skb, + struct nip_hdr_encap *head) +{ + struct nip_pseudo_header nph = {0}; + u8 *udp_hdr = skb_transport_header(skb); + unsigned short check_len = head->trans_hdr_len + head->usr_data_len; + + nph.nexthdr = IPPROTO_UDP; + nph.saddr = NIPCB(skb)->srcaddr; + nph.daddr = NIPCB(skb)->dstaddr; + nph.check_len = htons(check_len); + return nip_check_sum_build(udp_hdr, check_len, &nph); +} + +static struct sk_buff *_nip_alloc_skb(struct sock *sk, + struct nip_hdr_encap *head, + struct nip_pkt_seg_info *seg_info, + struct dst_entry *dst) +{ + int len; + int nip_hdr_len = get_nip_hdr_len(NIP_HDR_UDP, &head->saddr, &head->daddr); + struct sk_buff *skb; + + nip_hdr_len = nip_hdr_len == 0 ? NIP_HDR_MAX : nip_hdr_len; + len = NIP_ETH_HDR_LEN + nip_hdr_len + head->trans_hdr_len + seg_info->mid_usr_pkt_len; + skb = alloc_skb(len, 0); + if (!skb) { + DEBUG("%s: no space for skb", __func__); + return NULL; + } + + skb->protocol = htons(ETH_P_NEWIP); + skb->ip_summed = CHECKSUM_NONE; + skb->csum = 0; + skb->sk = sk; + + dst_hold(dst); + DEBUG("%s: malloc_len=%d, dst->__refcnt=%u", __func__, + len, atomic_read(&dst->__refcnt)); + skb_dst_set(skb, dst); + memset(NIPCB(skb), 0, sizeof(struct ninet_skb_parm)); + + return skb; +} + +static int _nip_udp_single_output(struct sock *sk, + struct nip_hdr_encap *head, + struct nip_pkt_seg_info *seg_info, + struct dst_entry *dst) +{ + int len; + int ret; + struct msghdr *from = (struct msghdr *)head->usr_data; + struct sk_buff *skb = _nip_alloc_skb(sk, head, seg_info, dst); + unsigned short check = 0; + + if (IS_ERR_OR_NULL(skb)) { + DEBUG("%s: skb alloc fail", __func__); + return -ENOMEM; + } + + /* Reserved Position of the Ethernet header (to be filled after the + * Ethernet header is delivered to the link layer) + */ + skb_reserve(skb, NIP_ETH_HDR_LEN); + + /* Fill in the Network-layer Header (newIP) */ + skb_reset_network_header(skb); + head->hdr_buf = skb->data; + nip_hdr_udp_encap(head); + skb_reserve(skb, head->hdr_buf_pos); + NIPCB(skb)->dstaddr = head->daddr; + NIPCB(skb)->srcaddr = head->saddr; + NIPCB(skb)->nexthdr = IPPROTO_UDP; + + /* Fill in the Transport Layer Header (UDP) */ + skb_reset_transport_header(skb); + nip_build_udp_hdr(head->sport, head->dport, + htons(head->trans_hdr_len + head->usr_data_len), + skb->data, htons(0)); + skb_reserve(skb, head->trans_hdr_len); + len = copy_from_iter(skb->data, head->usr_data_len, &from->msg_iter); + if (len < 0) { + /* The DST has been set to the SKB. When the SKB is released, + * the DST is automatically released + */ + DEBUG("%s: copy from iter fail.(datalen=%u)", + __func__, head->usr_data_len); + kfree_skb(skb); + return -EFBIG; + } + + /* insert check sum */ + check = nip_get_output_checksum(skb, head); + nip_build_udp_hdr(head->sport, head->dport, + htons(head->trans_hdr_len + head->usr_data_len), + skb->data - head->trans_hdr_len, htons(check)); + + /* Refresh the data/tail of the SKB after the packet copy is complete */ + skb_put(skb, head->usr_data_len); + skb->data = skb_network_header(skb); + skb->len = head->hdr_buf_pos + head->trans_hdr_len + + head->usr_data_len; + + /* Add the actual size of the current SKB to the SOCK send cache count + * and set destructor to __sock_wfree to reduce the SOCK send cache size + * when the SKB is released. + */ + skb->destructor = __sock_wfree; + refcount_add(skb->truesize, &sk->sk_wmem_alloc); + skb->priority = sk->sk_priority; + + ret = nip_send_skb(skb); + DEBUG("%s: newip output finish.(ret=%d, datalen=%u)", + __func__, ret, head->usr_data_len); + update_memory_rate(__func__); + return ret; +} + +int _nip_udp_output(struct sock *sk, void *from, int datalen, + int transhdrlen, const struct nip_addr *saddr, + ushort sport, const struct nip_addr *daddr, + ushort dport, struct dst_entry *dst) +{ + int i; + u32 ret = 0; + u32 mtu = dst_mtu(dst); + struct nip_pkt_seg_info seg_info = {0}; + struct nip_hdr_encap head = {0}; + int nip_hdr_len = get_nip_hdr_len(NIP_HDR_UDP, saddr, daddr); + + head.saddr = *saddr; + head.daddr = *daddr; + head.sport = sport; + head.dport = dport; + head.usr_data = from; + head.ttl = NIP_DEFAULT_TTL; + head.nexthdr = IPPROTO_UDP; + head.trans_hdr_len = transhdrlen; + + nip_hdr_len = nip_hdr_len == 0 ? NIP_HDR_MAX : nip_hdr_len; + nip_calc_pkt_frag_num(mtu, nip_hdr_len, datalen, &seg_info); + + /* Send intermediate data segments */ + for (i = 0; i < seg_info.mid_pkt_num; i++) { + head.usr_data_len = seg_info.mid_usr_pkt_len; + ret = _nip_udp_single_output(sk, &head, &seg_info, dst); + if (ret) + goto end; + } + + /* Send the last data segment */ + if (seg_info.last_pkt_num) { + head.usr_data_len = seg_info.last_usr_pkt_len; + ret = _nip_udp_single_output(sk, &head, &seg_info, dst); + } + +end: + return ret; +} + +static int nip_sk_dst_check(struct dst_entry *dst, + struct flow_nip *fln) +{ + int err = 0; + + if (!dst) + goto out; + + if (fln->flowin_oif && fln->flowin_oif != dst->dev->ifindex) + err = -EPERM; + +out: + return err; +} + +/* 1. Based on FLN, the routing table is searched to obtain the corresponding DST + * 2. The newIP address of the source end is obtained based on the routing table + * search result and stored in the fln->saddr + */ +static int nip_dst_lookup_tail(struct net *net, const struct sock *sk, + struct dst_entry **dst, struct flow_nip *fln) +{ + int err; + struct nip_rt_info *rt; + + if (!(*dst)) + *dst = nip_route_output(net, sk, fln); + + err = (*dst)->error; + if (err) { + rt = NULL; + DEBUG("%s: nip_route_output search error!", __func__); + goto out_err_release; + } + + err = nip_sk_dst_check(*dst, fln); + if (err) + goto out_err_release; + + rt = (struct nip_rt_info *)*dst; + if (*dst == &net->newip.nip_broadcast_entry->dst) { + if (!&fln->saddr) + fln->saddr = fln->daddr; + err = 0; + } else { + err = nip_route_get_saddr(net, rt, &fln->daddr, &fln->saddr); + } + + if (err) + goto out_err_release; + + return 0; + +out_err_release: + dst_release(*dst); + *dst = NULL; + + return err; +} + +struct dst_entry *nip_dst_lookup_flow(struct net *net, const struct sock *sk, + struct flow_nip *fln, + const struct nip_addr *final_dst) +{ + struct dst_entry *dst = NULL; + int err; + + err = nip_dst_lookup_tail(net, sk, &dst, fln); + if (err) + return ERR_PTR(err); + if (final_dst) + fln->daddr = *final_dst; + + return dst; +} + +struct dst_entry *nip_sk_dst_lookup_flow(struct sock *sk, struct flow_nip *fln) +{ + struct dst_entry *dst = NULL; + int err; + + err = nip_dst_lookup_tail(sock_net(sk), sk, &dst, fln); + if (err) + return ERR_PTR(err); + + return dst; +} + +int tcp_nip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl) +{ + int err = -EFAULT; + struct net *net = sock_net(sk); + struct nip_addr *saddr, *daddr; + struct dst_entry *dst; + struct flow_nip fln; + struct nip_hdr_encap head = {0}; + unsigned char hdr_buf[NIP_HDR_MAX]; /* Cache the newIP header */ + + rcu_read_lock(); + skb->protocol = htons(ETH_P_NEWIP); + skb->ip_summed = CHECKSUM_NONE; + skb->csum = 0; + saddr = &sk->sk_nip_rcv_saddr; + daddr = &sk->sk_nip_daddr; + + head.saddr = *saddr; + head.daddr = *daddr; + head.ttl = NIP_DEFAULT_TTL; + head.nexthdr = IPPROTO_TCP; + head.hdr_buf = hdr_buf; + nip_hdr_comm_encap(&head); + head.total_len = head.hdr_buf_pos + skb->len; + nip_update_total_len(&head, htons(head.total_len)); + + fln.daddr = sk->sk_nip_daddr; + dst = __sk_dst_check(sk, 0); + if (!dst) { + DEBUG("%s: no dst cache for sk, search newip rt.", __func__); + dst = nip_route_output(net, sk, &fln); + if (!dst) { + DEBUG("%s: cannot find dst.", __func__); + goto out; + } + sk_dst_set(sk, dst); + } + skb_dst_set_noref(skb, dst); + + /* build nwk header */ + skb_push(skb, head.hdr_buf_pos); + memcpy(skb->data, head.hdr_buf, head.hdr_buf_pos); + + skb_reset_network_header(skb); + NIPCB(skb)->srcaddr = *saddr; + NIPCB(skb)->dstaddr = *daddr; + NIPCB(skb)->nexthdr = head.nexthdr; + + skb->priority = sk->sk_priority; + head.total_len = skb->len; + err = nip_send_skb(skb); + if (err) + DEBUG("%s: failed to send skb, skb->len=%u", __func__, head.total_len); + else + DEBUG("%s: send skb ok, skb->len=%u", __func__, head.total_len); + +out: + rcu_read_unlock(); + return err; +} + +void tcp_nip_actual_send_reset(struct sock *sk, struct sk_buff *skb, u32 seq, + u32 ack_seq, u32 win, int rst, u32 priority) +{ + const struct tcphdr *th = tcp_hdr(skb); + struct tcphdr *t1; + struct sk_buff *buff; + struct flow_nip fln; + struct net *net; + struct nip_addr *saddr, *daddr; + unsigned int tot_len = sizeof(struct tcphdr); + struct nip_hdr_encap head = {0}; + unsigned char hdr_buf[NIP_HDR_MAX]; + struct dst_entry *dst; + int err; + + net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev); + + /* alloc skb */ + buff = alloc_skb(MAX_TCP_HEADER, priority); + if (!buff) { + DEBUG("%s: alloc_skb failed.\n", __func__); + return; + } + skb_reserve(buff, MAX_TCP_HEADER); + + buff->sk = sk; // sk could be NULL + saddr = &(NIPCB(skb)->dstaddr); + daddr = &(NIPCB(skb)->srcaddr); + + /* Fill in tcp header */ + t1 = skb_push(buff, sizeof(struct tcphdr)); + skb_reset_transport_header(buff); + memset(t1, 0, sizeof(*t1)); + t1->dest = th->source; + t1->source = th->dest; + t1->doff = tot_len / TCP_NUM_4; + t1->seq = htonl(seq); + t1->ack_seq = htonl(ack_seq); + t1->ack = !rst || !th->ack; + t1->rst = rst; + t1->window = htons(win); + t1->check = htons(nip_get_output_checksum_tcp(buff, *saddr, *daddr)); + DEBUG("%s: host dport==%u, net dport==%x, host sport==%u, net sport==%x\n", + __func__, ntohs(t1->dest), t1->dest, ntohs(t1->source), t1->source); + DEBUG("%s: host seq==%u, net seq==%x, host ack_seq==%u, net ack_seq==%x\n", + __func__, seq, t1->seq, ack_seq, t1->ack_seq); + + buff->protocol = htons(ETH_P_NEWIP); + buff->ip_summed = CHECKSUM_NONE; + buff->csum = 0; + + /* Fill in nip header */ + head.saddr = *saddr; + head.daddr = *daddr; + head.ttl = NIP_DEFAULT_TTL; + head.nexthdr = IPPROTO_TCP; + head.hdr_buf = hdr_buf; + nip_hdr_comm_encap(&head); + head.total_len = head.hdr_buf_pos + buff->len; + nip_update_total_len(&head, htons(head.total_len)); + + /* Check routine */ + fln.daddr = *daddr; + dst = nip_route_output(net, sk, &fln); // here, sk not used. + if (!dst) { + DEBUG("%s: cannot find dst.", __func__); + goto out; + } + skb_dst_set_noref(buff, dst); + + /* Build newip header */ + skb_push(buff, head.hdr_buf_pos); + memcpy(buff->data, head.hdr_buf, head.hdr_buf_pos); + + skb_reset_network_header(buff); + NIPCB(buff)->srcaddr = *saddr; + NIPCB(buff)->dstaddr = *daddr; + NIPCB(buff)->nexthdr = head.nexthdr; + + buff->priority = priority; + head.total_len = buff->len; + err = nip_send_skb(buff); + if (err) + DEBUG("%s: failed to send skb, skb->len=%u", __func__, head.total_len); + else + DEBUG("%s: send skb ok, skb->len=%u", __func__, head.total_len); + +out: + return; +} diff --git a/code/linux/net/newip/nip_sockglue.c b/code/linux/net/newip/nip_sockglue.c new file mode 100644 index 0000000..a94fbef --- /dev/null +++ b/code/linux/net/newip/nip_sockglue.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * + * NewIP INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. NewIP is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * The NewIP to API glue. + * + * Based on net/ipv4/ip_sockglue.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NIP_OPTNAME_MAX 255 + +static void __nip_set_sock_tos(struct sock *sk, int val) +{ + sk->sk_priority = rt_tos2priority(val); + sk_dst_reset(sk); +} + +static bool nip_setsockopt_needs_rtnl(int optname) +{ + switch (optname) { + case IP_MSFILTER: + return true; + } + return false; +} + +static bool nip_getsockopt_needs_rtnl(int optname) +{ + switch (optname) { + case IP_MSFILTER: + return true; + } + return false; +} + +static int do_nip_setsockopt(struct sock *sk, int level, int optname, + sockptr_t optval, unsigned int optlen) +{ + struct inet_sock *inet = inet_sk(sk); + int val = 0, err = 0; + bool needs_rtnl = nip_setsockopt_needs_rtnl(optname); + + if (optlen >= sizeof(int)) { + if (copy_from_sockptr(&val, optval, sizeof(val))) + return -EFAULT; + } else if (optlen >= sizeof(char)) { + unsigned char ucval; + + if (copy_from_sockptr(&ucval, optval, sizeof(ucval))) + return -EFAULT; + val = (int)ucval; + } + + if (needs_rtnl) + rtnl_lock(); + lock_sock(sk); + + switch (optname) { + case IP_TOS: + inet->tos = val; + __nip_set_sock_tos(sk, val); + break; + default: + err = -ENOPROTOOPT; + break; + } + + release_sock(sk); + if (needs_rtnl) + rtnl_unlock(); + + return err; +} + +int nip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, + unsigned int optlen) +{ + int err; + + if (level != SOL_IP) + return -ENOPROTOOPT; + + err = do_nip_setsockopt(sk, level, optname, optval, optlen); + + return err; +} + +static int do_nip_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct inet_sock *inet = inet_sk(sk); + bool needs_rtnl = nip_getsockopt_needs_rtnl(optname); + int val, err = 0; + int len; + + if (level != SOL_IP) + return -EOPNOTSUPP; + if (get_user(len, optlen)) + return -EFAULT; + if (len < 0) + return -EINVAL; + + if (needs_rtnl) + rtnl_lock(); + lock_sock(sk); + + switch (optname) { + case IP_TOS: + val = inet->tos; + break; + default: + err = -ENOPROTOOPT; + goto out; + } + + if (len < sizeof(int) && len > 0 && val >= 0 && val <= NIP_OPTNAME_MAX) { + unsigned char ucval = (unsigned char)val; + + len = 1; + if (put_user(len, optlen)) { + err = -EFAULT; + goto out; + } + if (copy_to_user(optval, &ucval, 1)) { + err = -EFAULT; + goto out; + } + } else { + len = min_t(unsigned int, sizeof(int), len); + if (put_user(len, optlen)) { + err = -EFAULT; + goto out; + } + if (copy_to_user(optval, &val, len)) { + err = -EFAULT; + goto out; + } + } +out: + release_sock(sk); + if (needs_rtnl) + rtnl_unlock(); + + return err; +} + +int nip_getsockopt(struct sock *sk, int level, + int optname, char __user *optval, int __user *optlen) +{ + return do_nip_getsockopt(sk, level, optname, optval, optlen); +} + -- Gitee