diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..0f69df1b7ee28e3a07ad4817b7f60747b78db003 --- /dev/null +++ b/LICENSE @@ -0,0 +1,48 @@ +NewIP - New IP Stack +Copyright (c) 2022 Huawei Device Co., Ltd. All rights reserved. + +NewIP is dual licensed: you can use it either under the terms of +the GPL V2, or the BSD2 license, at your option. +a) GNU General Public License version 2, (https://opensource.org/licenses/GPL-2.0) +This library 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. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public +License along with this library; if not, write to the Free +Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +MA 02110-1301 USA + +Alternatively, +b) The BSD2 License, (https://opensource.org/licenses/BSD-3-Clause) +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/code/net/newip/af_ninet.c b/code/net/newip/af_ninet.c new file mode 100644 index 0000000000000000000000000000000000000000..cda24d130c1a3c50d3b7b3049c7fa737c2477eac --- /dev/null +++ b/code/net/newip/af_ninet.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * + * NewIP INET socket protocol family + * Linux NewIP INET implementation + * + * Based on linux/net/ipv6/af_inet6.c + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for signal_pending() */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +MODULE_DESCRIPTION("NewiIP protocol stack for linux"); + +/* The inetsw_nip table contains everything that ninet_create needs to + * build a new socket + */ +static struct list_head inetsw_nip[SOCK_MAX]; +static DEFINE_SPINLOCK(inetsw_nip_lock); +/* count the socket number */ +atomic_t g_nip_socket_number = ATOMIC_INIT(0); + +static int disable_nip_mod; +module_param_named(disable, disable_nip_mod, int, 0444); +MODULE_PARM_DESC(disable, + "Disable NewIP module such that it is non_functional"); + +bool newip_mod_enabled(void) +{ + return disable_nip_mod == 0; +} +EXPORT_SYMBOL_GPL(newip_mod_enabled); + +static int ninet_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct inet_sock *inet; + struct sock *sk; + struct inet_protosw *answer; + struct proto *answer_prot; + unsigned char answer_flags; + int err; + int num; + + if (protocol < 0 || + protocol >= IPPROTO_MAX || + sock->type >= SOCK_MAX) + return -EINVAL; + + num = atomic_add_return(1, &g_nip_socket_number); + if (num > NIP_MAX_SOCKET_NUM) { + DEBUG("The number of socket is biger than 1024!"); + err = -EPERM; + goto number_sub; + } + + sock->state = SS_UNCONNECTED; + /* look for the requested type/protocol pair. */ + err = -ESOCKTNOSUPPORT; + rcu_read_lock(); + list_for_each_entry_rcu(answer, &inetsw_nip[sock->type], list) { + err = 0; + /* Check the non-wild matcg */ + if (protocol == answer->protocol) { + if (protocol != IPPROTO_IP) + break; + } else { + /* check for the two wild case. */ + if (protocol == IPPROTO_IP) { + protocol = answer->protocol; + break; + } + if (answer->protocol == IPPROTO_IP) + break; + } + err = -EPROTONOSUPPORT; + } + + if (err) + goto out_rcu_unlock; + + err = -EPERM; + + sock->ops = answer->ops; + answer_prot = answer->prot; + answer_flags = answer->flags; + rcu_read_unlock(); + + WARN_ON(!answer_prot->slab); + + err = -ENOBUFS; + sk = sk_alloc(net, PF_NINET, GFP_KERNEL, answer_prot, kern); + if (!sk) + goto number_sub; + + sock_init_data(sock, sk); + + err = 0; + if (answer_flags & INET_PROTOSW_REUSE) + sk->sk_reuse = SK_CAN_REUSE; + inet = inet_sk(sk); + inet->is_icsk = (answer_flags & INET_PROTOSW_ICSK) != 0; + inet->nodefrag = 0; + + if (sock->type == SOCK_RAW) { + inet->inet_num = protocol; + if (protocol == IPPROTO_RAW) + inet->hdrincl = 1; + } + + sk->sk_destruct = inet_sock_destruct; + sk->sk_family = PF_NINET; + sk->sk_protocol = protocol; + sk->sk_backlog_rcv = answer->prot->backlog_rcv; + sk->sk_nip_daddr = nip_any_addr; + sk->sk_nip_rcv_saddr = nip_any_addr; + + inet->uc_ttl = -1; + inet->mc_loop = 1; + inet->mc_ttl = 1; + inet->mc_all = 1; + inet->mc_index = 0; + inet->mc_list = NULL; + inet->rcv_tos = 0; + sk_refcnt_debug_inc(sk); + + if (inet->inet_num) { + inet->inet_sport = htons(inet->inet_num); + err = sk->sk_prot->hash(sk); + if (err) { + sk_common_release(sk); + goto number_sub; + } + } + if (sk->sk_prot->init) { + err = sk->sk_prot->init(sk); + if (err) { + sk_common_release(sk); + goto number_sub; + } + } +out: + DEBUG("The final number of socket is: %d", num); + return err; +out_rcu_unlock: + rcu_read_unlock(); +number_sub: + atomic_dec_if_positive(&g_nip_socket_number); + num = atomic_read(&g_nip_socket_number); + DEBUG("The final number of socket is: %d", num); + goto out; +} + +int ninet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sockaddr_nin *addr = (struct sockaddr_nin *)uaddr; + struct sock *sk = sock->sk; + struct inet_sock *inet = inet_sk(sk); + struct net *net = sock_net(sk); + u_short snum; + int err = 0; + + /* If the socket has its own bind function then use it */ + if (sk->sk_prot->bind) + return sk->sk_prot->bind(sk, uaddr, addr_len); + + if (addr_len < sizeof(struct sockaddr_nin)) + return -EINVAL; + + snum = ntohs(addr->sin_port); + if (snum && snum < PROT_SOCK) + return -EACCES; + + if (nip_bind_addr_check(net, &addr->sin_addr) == false) { + DEBUG("%s: binding-addr invalid.", __func__); + return -EADDRNOTAVAIL; + } + lock_sock(sk); + + /* check these errors (active socket, double bind) */ + if (sk->sk_state != TCP_CLOSE || inet->inet_num) { + err = -EINVAL; + goto out; + } + + sk->sk_nip_rcv_saddr = addr->sin_addr; + + /* make sure we are allowed to bind here */ + if ((snum || !inet->bind_address_no_port) && + sk->sk_prot->get_port(sk, snum)) { + inet->inet_saddr = 0; + err = -EADDRINUSE; + goto out; + } + inet->inet_sport = htons(inet->inet_num); + inet->inet_daddr = 0; + inet->inet_dport = 0; + sk_dst_reset(sk); + +out: + release_sock(sk); + return err; +} + +/* Function + * Move a socket into listening state. + * Parameter + * sock: The socket + * backlog: Specifies the number of clients that use a three-way handshake + * to establish a TCP connection + */ +int ninet_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + unsigned char old_state; + int err; + + lock_sock(sk); + + err = -EINVAL; + if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM) + goto out; + + old_state = sk->sk_state; + if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN))) + goto out; + + WRITE_ONCE(sk->sk_max_ack_backlog, backlog); + /* Really, if the socket is already in listen state + * we can only allow the backlog to be adjusted. + */ + if (old_state != TCP_LISTEN) { + err = inet_csk_listen_start(sk, backlog); + if (err) + goto out; + } + err = 0; + +out: + release_sock(sk); + return err; +} + +int ninet_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return -EINVAL; + + atomic_dec_if_positive(&g_nip_socket_number); + return inet_release(sock); +} + +void ninet_destroy_sock(struct sock *sk) +{ + ; +} + +int ninet_getname(struct socket *sock, struct sockaddr *uaddr, + int peer) +{ + struct sock *sk = sock->sk; + struct inet_sock *inet = inet_sk(sk); + DECLARE_SOCKADDR(struct sockaddr_nin *, sin, uaddr); + + sin->sin_family = AF_NINET; + if (peer) { + if (!inet->inet_dport) + return -ENOTCONN; + if (((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)) && + peer == 1) + return -ENOTCONN; + sin->sin_port = inet->inet_dport; + sin->sin_addr = sk->sk_nip_daddr; + } else { + sin->sin_port = inet->inet_sport; + sin->sin_addr = sk->sk_nip_rcv_saddr; + } + return sizeof(*sin); +} + +static long ninet_wait_for_connect(struct sock *sk, long timeo, int writebias) +{ + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + add_wait_queue(sk_sleep(sk), &wait); + sk->sk_write_pending += writebias; + + /* Basic assumption: if someone sets sk->sk_err, he _must_ + * change state of the socket from TCP_SYN_*. + * Connect() does not allow to get error notifications + * without closing the socket. + */ + while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { + release_sock(sk); + timeo = wait_woken(&wait, TASK_INTERRUPTIBLE, timeo); + lock_sock(sk); + if (signal_pending(current) || !timeo) + break; + } + remove_wait_queue(sk_sleep(sk), &wait); + sk->sk_write_pending -= writebias; + return timeo; +} + +/* Function + * The client socket layer is used to establish connection requests + * Parameter + * sock: The socket + * uaddr:The destination address + */ +int __ninet_stream_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + int err; + long timeo; + + if (uaddr) { + if (addr_len < sizeof(uaddr->sa_family)) + return -EINVAL; + } + + switch (sock->state) { + default: + err = -EINVAL; + goto out; + case SS_CONNECTED: + err = -EISCONN; + goto out; + case SS_CONNECTING: + err = -EALREADY; + break; + case SS_UNCONNECTED: + err = -EISCONN; + if (sk->sk_state != TCP_CLOSE) + goto out; + /* Call the tcp_nip_connect function */ + err = sk->sk_prot->connect(sk, uaddr, addr_len); + if (err < 0) + goto out; + /* Switch to connecting, and then perform subsequent operations */ + sock->state = SS_CONNECTING; + err = -EINPROGRESS; + break; + } + + /* Get blocking time */ + timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { + int writebias = 0; + /* Error code is set above */ + if (!timeo || !ninet_wait_for_connect(sk, timeo, writebias)) + goto out; + + err = sock_intr_errno(timeo); + if (signal_pending(current)) + goto out; + } + + if (sk->sk_state == TCP_CLOSE) + goto sock_error; + sock->state = SS_CONNECTED; + err = 0; + +out: + return err; +sock_error: + err = sock_error(sk) ? : -ECONNABORTED; + sock->state = SS_DISCONNECTING; + goto out; +} + +int ninet_stream_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + int err; + + lock_sock(sock->sk); + err = __ninet_stream_connect(sock, uaddr, addr_len, flags); + release_sock(sock->sk); + return err; +} + +int ninet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + struct net *net = sock_net(sk); + + DEBUG("%s: cmd=0x%x.", __func__, cmd); + switch (cmd) { + case SIOCADDRT: + case SIOCDELRT: { + struct nip_rtmsg rtmsg; + + if (copy_from_user(&rtmsg, (void __user *)arg, sizeof(rtmsg))) { + DEBUG("%s: fail to copy route cfg data.", __func__); + return -EFAULT; + } + return nip_route_ioctl(net, cmd, &rtmsg); + } + case SIOCSIFADDR: + return nip_addrconf_add_ifaddr(net, (void __user *)arg); + case SIOCDIFADDR: + return nip_addrconf_del_ifaddr(net, (void __user *)arg); + case SIOCGIFADDR: + return nip_addrconf_get_ifaddr(net, cmd, (void __user *)arg); + + default: + if (!sk->sk_prot->ioctl) { + DEBUG("%s: sock sk_prot ioctl is null, cmd=0x%x.", __func__, cmd); + return -ENOIOCTLCMD; + } + return sk->sk_prot->ioctl(sk, cmd, arg); + } +} + +#ifdef CONFIG_COMPAT +struct compat_nip_rtmsg { + struct nip_addr rtmsg_dst; + struct nip_addr rtmsg_src; + struct nip_addr rtmsg_gateway; + char dev_name[10]; + unsigned int rtmsg_type; + int rtmsg_ifindex; + unsigned int rtmsg_metric; + unsigned int rtmsg_info; /* long convert to int */ + unsigned int rtmsg_flags; +}; + +static int ninet_compat_routing_ioctl(struct sock *sk, unsigned int cmd, + struct compat_nip_rtmsg __user *ur) +{ + struct nip_rtmsg rt; + + if (copy_from_user(&rt.rtmsg_dst, &ur->rtmsg_dst, 3 * sizeof(struct nip_addr)) || + copy_from_user(&rt.dev_name, &ur->dev_name, sizeof(rt.dev_name)) || + get_user(rt.rtmsg_type, &ur->rtmsg_type) || + get_user(rt.rtmsg_ifindex, &ur->rtmsg_ifindex) || + get_user(rt.rtmsg_metric, &ur->rtmsg_metric) || + get_user(rt.rtmsg_info, &ur->rtmsg_info) || + get_user(rt.rtmsg_flags, &ur->rtmsg_flags)) { + DEBUG("%s: fail to convert input para, cmd=0x%x.", __func__, cmd); + return -EFAULT; + } + + DEBUG("%s: cmd=0x%x.", __func__, cmd); + return nip_route_ioctl(sock_net(sk), cmd, &rt); +} + +int ninet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + void __user *argp = compat_ptr(arg); + struct sock *sk = sock->sk; + + switch (cmd) { + case SIOCADDRT: + case SIOCDELRT: + return ninet_compat_routing_ioctl(sk, cmd, argp); + default: + return -ENOIOCTLCMD; + } +} +EXPORT_SYMBOL_GPL(ninet_compat_ioctl); +#endif /* CONFIG_COMPAT */ + +/* register new IP socket */ +const struct proto_ops ninet_dgram_ops = { + .family = PF_NINET, + .owner = THIS_MODULE, + .release = ninet_release, + .bind = ninet_bind, + .connect = inet_dgram_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = ninet_getname, + .poll = datagram_poll, + .ioctl = ninet_ioctl, + .gettstamp = sock_gettstamp, + .listen = sock_no_listen, + .shutdown = inet_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, + .set_peek_off = sk_set_peek_off, +#ifdef CONFIG_COMPAT + .compat_ioctl = ninet_compat_ioctl, +#endif +}; + +const struct proto_ops ninet_stream_ops = { + .family = PF_NINET, + .owner = THIS_MODULE, + .release = ninet_release, + .bind = ninet_bind, + .connect = ninet_stream_connect, + .socketpair = sock_no_socketpair, + .accept = inet_accept, + .getname = ninet_getname, + .poll = tcp_poll, + .ioctl = ninet_ioctl, + .listen = ninet_listen, + .shutdown = inet_shutdown, + .setsockopt = sock_common_setsockopt, + .getsockopt = sock_common_getsockopt, + .sendmsg = inet_sendmsg, + .recvmsg = inet_recvmsg, + .mmap = sock_no_mmap, + .sendpage = inet_sendpage, +#ifdef CONFIG_COMPAT + .compat_ioctl = ninet_compat_ioctl, +#endif +}; + +static const struct net_proto_family ninet_family_ops = { + .family = PF_NINET, + .create = ninet_create, + .owner = THIS_MODULE, +}; + +int ninet_register_protosw(struct inet_protosw *p) +{ + struct list_head *lh; + struct inet_protosw *answer; + struct list_head *last_perm; + int protocol = p->protocol; + int ret; + + spin_lock_bh(&inetsw_nip_lock); + + ret = -EINVAL; + if (p->type >= SOCK_MAX) + goto out_illegal; + + /* If we are trying to override a permanent protocol, bail. */ + answer = NULL; + ret = -EPERM; + last_perm = &inetsw_nip[p->type]; + list_for_each(lh, &inetsw_nip[p->type]) { + answer = list_entry(lh, struct inet_protosw, list); + + /* Check only the non-wild match. */ + if (answer->flags & INET_PROTOSW_PERMANENT) { + if (protocol == answer->protocol) + break; + last_perm = lh; + } + + answer = NULL; + } + if (answer) + goto out_permanent; + + list_add_rcu(&p->list, last_perm); + ret = 0; +out: + spin_unlock_bh(&inetsw_nip_lock); + return ret; + +out_permanent: + pr_err("Attempt to override permanent protocol %d\n", protocol); + goto out; + +out_illegal: + pr_err("Ignoring attempt to register invalid socket type %d\n", + p->type); + goto out; +} + +void ninet_unregister_protosw(struct inet_protosw *p) +{ + if (INET_PROTOSW_PERMANENT & p->flags) { + pr_err("Attempt to unregister permanent protocol %d\n", + p->protocol); + } else { + spin_lock_bh(&inetsw_nip_lock); + list_del_rcu(&p->list); + spin_unlock_bh(&inetsw_nip_lock); + + synchronize_net(); + } +} + +int ninet_sk_rebuild_header(struct sock *sk) +{ + return 0; +} + +/* register to data link layer */ +static struct packet_type nip_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_NEWIP), + .func = nip_rcv, +}; + +static int __init nip_packet_init(void) +{ + dev_add_pack(&nip_packet_type); + return 0; +} + +static int __net_init ninet_net_init(struct net *net) +{ + int err = 0; + return err; +} + +static void __net_exit ninet_net_exit(struct net *net) +{ + ; +} + +static struct pernet_operations ninet_net_ops = { + .init = ninet_net_init, + .exit = ninet_net_exit, +}; + +static int __init ninet_init(void) +{ + struct list_head *r; + int err = 0; + + sock_skb_cb_check_size(sizeof(struct ninet_skb_parm)); + + DEBUG("NET: start to init nip network.\n"); + /* register the socket-side information for ninet_create */ + for (r = &inetsw_nip[0]; r < &inetsw_nip[SOCK_MAX]; ++r) + INIT_LIST_HEAD(r); + + if (disable_nip_mod) { + DEBUG("Loaded, but adminstratively disabled,"); + DEBUG("reboot required to enable\n"); + goto out; + } + + err = proto_register(&tcp_nip_prot, 1); + if (err) + goto out; + + err = proto_register(&nip_udp_prot, 1); + if (err) { + DEBUG_TRACE("failed to register udp proto!\n"); + goto out_udp_register_fail; + } + + err = sock_register(&ninet_family_ops); + if (err) { + DEBUG_TRACE("failed to register newip_family_ops!"); + goto out_sock_register_fail; + } + + err = register_pernet_subsys(&ninet_net_ops); + if (err) { + DEBUG_TRACE("failed to register ninet_net_ops!\n"); + goto register_pernet_fail; + } + + err = nip_icmp_init(); + if (err) { + DEBUG_TRACE("nip_icmp_init failed!\n"); + goto nip_icmp_fail; + } + + err = nndisc_init(); + if (err) { + DEBUG_TRACE("nndisc_init failed!\n"); + goto nndisc_fail; + } + + err = nip_route_init(); + if (err) + goto nip_route_fail; + + err = nip_addrconf_init(); + if (err) + goto nip_addr_fail; + + err = nip_udp_init(); + if (err) { + DEBUG_TRACE("failed to init udp layer!\n"); + goto udp_fail; + } + + err = tcp_nip_init(); + if (err) { + DEBUG("failed to init tcp layer!\n"); + goto tcp_fail; + } else { + DEBUG("nip_tcp_init ok!"); + } + + err = nip_packet_init(); + if (err) { + DEBUG_TRACE("failed to register to l2 layer!\n"); + goto nip_packet_fail; + } + + DEBUG("NewIP: init newip address family ok!"); + +out: + return err; + +nip_packet_fail: +udp_fail: +tcp_fail: + nip_addrconf_cleanup(); +nip_addr_fail: + nip_route_cleanup(); +nip_route_fail: +nndisc_fail: +nip_icmp_fail: + unregister_pernet_subsys(&ninet_net_ops); +register_pernet_fail: + sock_unregister(PF_NINET); +out_sock_register_fail: + proto_unregister(&nip_udp_prot); +out_udp_register_fail: + DEBUG_TRACE("newip family init failed!!!\n"); + goto out; +} + +module_init(ninet_init); + +MODULE_ALIAS_NETPROTO(PF_NINET); +