/*
 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
 *
 * 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.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * 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 HOLDER 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.
 */

#include "lwip/sockets.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/priv/sockets_priv.h"
#include "lwip/prot/dhcp.h"
#include "lwip/dhcp.h"
#include "lwip/if_api.h"

#if !LWIP_COMPAT_SOCKETS
#if LWIP_SOCKET

#define CHECK_NULL_PTR(ptr) do { if ((ptr) == NULL) { set_errno(EFAULT); return -1; } } while (0)

int accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
    return lwip_accept(s, addr, addrlen);
}

int bind(int s, const struct sockaddr *name, socklen_t namelen)
{
    CHECK_NULL_PTR(name);
    if (namelen < sizeof(*name)) {
        set_errno(EINVAL);
        return -1;
    }
    return lwip_bind(s, name, namelen);
}

int shutdown(int s, int how)
{
    return lwip_shutdown(s, how);
}

int getpeername(int s, struct sockaddr *name, socklen_t *namelen)
{
    CHECK_NULL_PTR(name);
    CHECK_NULL_PTR(namelen);
    return lwip_getpeername(s, name, namelen);
}

int getsockname(int s, struct sockaddr *name, socklen_t *namelen)
{
    CHECK_NULL_PTR(name);
    CHECK_NULL_PTR(namelen);
    return lwip_getsockname(s, name, namelen);
}

int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
{
    return lwip_getsockopt(s, level, optname, optval, optlen);
}

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
{
    return lwip_setsockopt(s, level, optname, optval, optlen);
}

int closesocket(int s)
{
    return lwip_close(s);
}

int connect(int s, const struct sockaddr *name, socklen_t namelen)
{
    CHECK_NULL_PTR(name);
    if (namelen < sizeof(*name)) {
        set_errno(EINVAL);
        return -1;
    }
    return lwip_connect(s, name, namelen);
}

int listen(int s, int backlog)
{
    return lwip_listen(s, backlog);
}

ssize_t recv(int s, void *mem, size_t len, int flags)
{
    CHECK_NULL_PTR(mem);
    return lwip_recv(s, mem, len, flags);
}

ssize_t recvfrom(int s, void *mem, size_t len, int flags,
                 struct sockaddr *from, socklen_t *fromlen)
{
    CHECK_NULL_PTR(mem);
    return lwip_recvfrom(s, mem, len, flags, from, fromlen);
}

ssize_t recvmsg(int s, struct msghdr *message, int flags)
{
    CHECK_NULL_PTR(message);
    if (message->msg_iovlen) {
        CHECK_NULL_PTR(message->msg_iov);
    }
    return lwip_recvmsg(s, message, flags);
}

ssize_t send(int s, const void *dataptr, size_t size, int flags)
{
    CHECK_NULL_PTR(dataptr);
    return  lwip_send(s, dataptr, size, flags);
}

ssize_t sendmsg(int s, const struct msghdr *message, int flags)
{
    return lwip_sendmsg(s, message, flags);
}

ssize_t sendto(int s, const void *dataptr, size_t size, int flags,
               const struct sockaddr *to, socklen_t tolen)
{
    CHECK_NULL_PTR(dataptr);
    if (to && tolen < sizeof(*to)) {
        set_errno(EINVAL);
        return -1;
    }
    return lwip_sendto(s, dataptr, size, flags, to, tolen);
}

int socket(int domain, int type, int protocol)
{
    return lwip_socket(domain, type, protocol);
}

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)
{
    return lwip_inet_ntop(af, src, dst, size);
}

int inet_pton(int af, const char *src, void *dst)
{
    return lwip_inet_pton(af, src, dst);
}

#ifndef LWIP_INET_ADDR_FUNC
in_addr_t inet_addr(const char *cp)
{
    return ipaddr_addr(cp);
}
#endif

#ifndef LWIP_INET_NTOA_FUNC
char *inet_ntoa(struct in_addr addr)
{
    return ip4addr_ntoa((const ip4_addr_t*)&(addr));
}
#endif

#ifndef LWIP_INET_ATON_FUNC
int inet_aton(const char *cp, struct in_addr *addr)
{
    return ip4addr_aton(cp, (ip4_addr_t*)addr);
}
#endif

int ioctlsocket(int s, long cmd, void *argp)
{
    return lwip_ioctl(s, cmd, argp);
}

#ifdef LWIP_SOCKET_READ_FUNC
ssize_t read(int fd, void *buf, size_t len)
{
    return lwip_read(fd, buf, len);
}
#endif

#ifdef LWIP_SOCKET_WRITE_FUNC
ssize_t write(int fd, const void *buf, size_t len)
{
    return lwip_write(fd, buf, len);
}
#endif

#ifdef LWIP_SOCKET_CLOSE_FUNC
int close(int fd)
{
    return lwip_close(fd);
}
#endif

#if LWIP_SOCKET_IOCTL
#ifdef LWIP_SOCKET_IOCTL_FUNC
int ioctl(int fd, int req, ...)
{
    UINTPTR arg = 0;
    va_list ap;
    va_start(ap, req);
    arg = va_arg(ap, UINTPTR);
    va_end(ap);
    return lwip_ioctl(fd, (long)req, (void *)arg);
}
#endif
#endif

#if LWIP_SOCKET_FCNTL
#ifdef LWIP_SOCKET_FCNTL_FUNC
int fcntl(int fd, int cmd, ...)
{
    int val = 0;
    va_list ap;
    va_start(ap, cmd);
    val = va_arg(ap, int);
    va_end(ap);
    return lwip_fcntl(fd, cmd, val);
}
#endif
#endif

#if LWIP_SOCKET_SELECT
#ifdef LWIP_SOCKET_SELECT_FUNC
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout)
{
    return lwip_select(maxfdp1, readset, writeset, exceptset, timeout);
}
#endif
#endif

#if LWIP_SOCKET_POLL
#ifdef LWIP_SOCKET_POLL_FUNC
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
{
    return lwip_poll(fds, nfds, timeout);
}
#endif
#endif

unsigned int if_nametoindex(const char *ifname)
{
    return lwip_if_nametoindex(ifname);
}

#endif
#endif /* !LWIP_COMPAT_SOCKETS */

#define IOCTL_CMD_CASE_HANDLER() do {                      \
    err_t  err;                                            \
    struct lwip_ioctl_apimsg msg;                          \
    msg.sock = sock;                                       \
    msg.cmd = cmd;                                         \
    msg.argp = argp;                                       \
                                                           \
    err = tcpip_api_call(lwip_do_ioctl_impl, &msg.call);   \
    if (err != ENOSYS) {                                   \
        sock_set_errno(sock, err);                         \
        done_socket(sock);                                 \
        return -(err != ERR_OK);                           \
    }                                                      \
} while (0)

struct lwip_ioctl_apimsg {
    struct tcpip_api_call_data call;
    struct lwip_sock *sock;
    long cmd;
    void *argp;
};

static err_t lwip_do_ioctl_impl(struct tcpip_api_call_data *call);

#include "../api/sockets.c"

static u8_t lwip_ioctl_internal_SIOCGIFCONF(struct ifreq *ifr)
{
    struct ifconf *ifc = NULL;
    struct netif *netif = NULL;
    struct ifreq ifreq;
    struct sockaddr_in *sock_in = NULL;
    int pos;
    int len;
    int ret;

    /* Format the caller's buffer. */
    ifc = (struct ifconf *)ifr;
    len = ifc->ifc_len;

    /* Loop over the interfaces, and write an info block for each. */
    pos = 0;
    for (netif = netif_list; netif != NULL; netif = netif->next) {
        if (ifc->ifc_buf == NULL) {
            pos = (pos + (int)sizeof(struct ifreq));
            continue;
        }

        if (len < (int)sizeof(ifreq)) {
            break;
        }
        (void)memset_s(&ifreq, sizeof(struct ifreq), 0, sizeof(struct ifreq));
        if (netif->link_layer_type == LOOPBACK_IF) {
            ret = snprintf_s(ifreq.ifr_name, IFNAMSIZ, (IFNAMSIZ - 1), "%.2s", netif->name);
            if ((ret <= 0) || (ret >= IFNAMSIZ)) {
                LWIP_DEBUGF(NETIF_DEBUG, ("lwip_ioctl: snprintf_s ifr_name failed."));
                return ENOBUFS;
            }
        } else {
            ret = snprintf_s(ifreq.ifr_name, IFNAMSIZ, (IFNAMSIZ - 1), "%s", netif_get_name(netif));
            if ((ret <= 0) || (ret >= IFNAMSIZ)) {
                LWIP_DEBUGF(NETIF_DEBUG, ("lwip_ioctl: snprintf_s ifr_name failed."));
                return ENOBUFS;
            }
        }

        sock_in = (struct sockaddr_in *)&ifreq.ifr_addr;
        sock_in->sin_family = AF_INET;
        sock_in->sin_addr.s_addr = ip_2_ip4(&netif->ip_addr)->addr;
        if (memcpy_s(ifc->ifc_buf + pos, sizeof(struct ifreq), &ifreq, sizeof(struct ifreq)) != EOK) {
            return ENOBUFS;
        }
        pos = pos + (int)sizeof(struct ifreq);
        len = len - (int)sizeof(struct ifreq);
    }

    ifc->ifc_len = pos;

    return 0;
}

static u8_t lwip_ioctl_internal_SIOCGIFADDR(struct ifreq *ifr)
{
    struct netif *netif = NULL;
    struct sockaddr_in *sock_in = NULL;

    /* get netif ipaddr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else {
        sock_in = (struct sockaddr_in *)&ifr->ifr_addr;
        sock_in->sin_family = AF_INET;
        sock_in->sin_addr.s_addr = ip_2_ip4(&netif->ip_addr)->addr;
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFNETMASK(struct ifreq *ifr)
{
    struct netif *netif = NULL;
    struct sockaddr_in *sock_in = NULL;

    /* get netif netmask */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else {
        sock_in = (struct sockaddr_in *)&ifr->ifr_netmask;
        sock_in->sin_family = AF_INET;
        sock_in->sin_addr.s_addr = ip_2_ip4(&netif->netmask)->addr;
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFHWADDR(struct ifreq *ifr)
{
    struct netif *netif = NULL;

    /* get netif hw addr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    }
#if LWIP_HAVE_LOOPIF
    else if (netif->link_layer_type == LOOPBACK_IF) {
        return EPERM;
    }
#endif /* LWIP_HAVE_LOOPIF */
    else {
        if (memcpy_s((void *)ifr->ifr_hwaddr.sa_data, sizeof(ifr->ifr_hwaddr.sa_data),
                     (void *)netif->hwaddr, netif->hwaddr_len) != EOK) {
            return EINVAL;
        }
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCSIFFLAGS(struct ifreq *ifr)
{
    struct netif *netif = NULL;

    /* set netif hw addr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    }
#if LWIP_HAVE_LOOPIF
    else if (netif->link_layer_type == LOOPBACK_IF) {
        return EPERM;
    }
#endif /* LWIP_HAVE_LOOPIF */
    else {
        if (((unsigned short)ifr->ifr_flags & IFF_UP) && !(netif->flags & NETIF_FLAG_UP)) {
            (void)netif_set_up(netif);
        } else if (!((unsigned short)ifr->ifr_flags & IFF_UP) && (netif->flags & NETIF_FLAG_UP)) {
            (void)netif_set_down(netif);
        }
        if (((unsigned short)ifr->ifr_flags & IFF_RUNNING) && !(netif->flags & NETIF_FLAG_LINK_UP)) {
            (void)netif_set_link_up(netif);
        } else if (!((unsigned short)ifr->ifr_flags & IFF_RUNNING) && (netif->flags & NETIF_FLAG_LINK_UP)) {
            (void)netif_set_link_down(netif);
        }

        if ((unsigned short)ifr->ifr_flags & IFF_BROADCAST) {
            netif->flags |= NETIF_FLAG_BROADCAST;
        } else {
            netif->flags = netif->flags & (~NETIF_FLAG_BROADCAST);
        }
        if ((unsigned short)ifr->ifr_flags & IFF_NOARP) {
            netif->flags = (netif->flags & (~NETIF_FLAG_ETHARP));
        } else {
            netif->flags |= NETIF_FLAG_ETHARP;
        }

        if ((unsigned short)ifr->ifr_flags & IFF_MULTICAST) {
#if LWIP_IGMP
            netif->flags |= NETIF_FLAG_IGMP;
#endif /* LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
            netif->flags |= NETIF_FLAG_MLD6;
#endif /* LWIP_IPV6_MLD */
        } else {
#if LWIP_IGMP
            netif->flags = (netif->flags & ~NETIF_FLAG_IGMP);
#endif /* LWIP_IGMP */
#if LWIP_IPV6 && LWIP_IPV6_MLD
            netif->flags = (netif->flags & ~NETIF_FLAG_MLD6);
#endif /* LWIP_IPV6_MLD */
        }

#if LWIP_DHCP
        if ((unsigned short)ifr->ifr_flags & IFF_DYNAMIC) {
            (void)dhcp_start(netif);
        } else {
            dhcp_stop(netif);
#if !LWIP_DHCP_SUBSTITUTE
            dhcp_cleanup(netif);
#endif
        }
#endif

#if LWIP_NETIF_PROMISC
        if (((unsigned short)ifr->ifr_flags & IFF_PROMISC)) {
            netif->flags |= NETIF_FLAG_PROMISC;
        } else {
            netif->flags &= ~NETIF_FLAG_PROMISC;
        }
        if (netif->drv_config) {
            netif->drv_config(netif, IFF_PROMISC, !!((unsigned short)ifr->ifr_flags & IFF_PROMISC));
        }
#endif /* LWIP_NETIF_PROMISC */
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFFLAGS(struct ifreq *ifr)
{
    struct netif *netif = NULL;

    /* set netif hw addr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else {
        if (netif->flags & NETIF_FLAG_UP) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_UP;
        } else {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) & ~IFF_UP;
        }
        if (netif->flags & NETIF_FLAG_LINK_UP) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_RUNNING;
        } else {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) & ~IFF_RUNNING;
        }
        if (netif->flags & NETIF_FLAG_BROADCAST) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_BROADCAST;
        } else {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) & ~IFF_BROADCAST;
        }
        if (netif->flags & NETIF_FLAG_ETHARP) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) & ~IFF_NOARP;
        } else {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_NOARP;
        }

#if LWIP_IGMP || LWIP_IPV6_MLD
        if (
#if LWIP_IGMP
            (netif->flags & NETIF_FLAG_IGMP)
#endif /* LWIP_IGMP */
#if LWIP_IGMP && LWIP_IPV6_MLD
            ||
#endif /* LWIP_IGMP && LWIP_IPV6_MLD */
#if LWIP_IPV6_MLD
            (netif->flags & NETIF_FLAG_MLD6)
#endif /* LWIP_IPV6_MLD */
                ) {
            ifr->ifr_flags = (short)((unsigned short)ifr->ifr_flags | IFF_MULTICAST);
        } else {
            ifr->ifr_flags = (short)((unsigned short)ifr->ifr_flags & (~IFF_MULTICAST));
        }
#endif /* LWIP_IGMP || LWIP_IPV6_MLD */

#if LWIP_DHCP
        if (dhcp_supplied_address(netif)) {
            ifr->ifr_flags = (short)((unsigned short)ifr->ifr_flags | IFF_DYNAMIC);
        } else {
            ifr->ifr_flags = (short)((unsigned short)ifr->ifr_flags & (~IFF_DYNAMIC));
        }
#endif

#if LWIP_HAVE_LOOPIF
        if (netif->link_layer_type == LOOPBACK_IF) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_LOOPBACK;
        }
#endif

#if LWIP_NETIF_PROMISC
        if (netif->flags & NETIF_FLAG_PROMISC) {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) | IFF_PROMISC;
        } else {
            ifr->ifr_flags = ((unsigned short)(ifr->ifr_flags)) & ~IFF_PROMISC;
        }
#endif /* LWIP_NETIF_PROMISC */

        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFNAME(struct ifreq *ifr)
{
    struct netif *netif = NULL;
    int ret;

    for (netif = netif_list; netif != NULL; netif = netif->next) {
        if (ifr->ifr_ifindex == netif_get_index(netif)) {
            break;
        }
    }

    if (netif == NULL) {
        return ENODEV;
    } else {
        if (netif->link_layer_type == LOOPBACK_IF) {
            ret = snprintf_s(ifr->ifr_name, IFNAMSIZ, (IFNAMSIZ - 1), "%.2s", netif->name);
            if ((ret <= 0) || (ret >= IFNAMSIZ)) {
                return ENOBUFS;
            }
        } else {
            ret = snprintf_s(ifr->ifr_name, IFNAMSIZ, (IFNAMSIZ - 1), "%s", netif_get_name(netif));
            if ((ret <= 0) || (ret >= IFNAMSIZ)) {
                return ENOBUFS;
            }
        }
        return 0;
    }
}

static u8_t lwip_validate_ifname(const char *name, u8_t *let_pos)
{
    unsigned short num_pos = 0;
    unsigned short letter_pos = 0;
    unsigned short pos = 0;
    u8_t have_num = 0;

    /* if the first position of variable name is not letter, such as '6eth2' */
    if (!((*name >= 'a' && *name <= 'z') || (*name >= 'A' && *name <= 'Z'))) {
        return 0;
    }

    /* check if the position of letter is bigger than the the position of digital */
    while (*name != '\0') {
        if ((*name >= '0') && (*name <= '9')) {
            num_pos = pos;
            have_num = 1;
        } else if (((*name >= 'a') && (*name <= 'z')) || ((*name >= 'A') && (*name <= 'Z'))) {
            letter_pos = pos;
            if (have_num != 0) {
                return 0;
            }
        } else {
            return 0;
        }
        pos++;
        name++;
    }

    /* for the speacil case as all position of variable name is letter, such as 'ethabc' */
    if (num_pos == 0) {
        return 0;
    }

    /* cheak if the digital in the variable name is bigger than 255, such as 'eth266' */
    if (atoi(name - (pos - letter_pos - 1)) > 255) {
        return 0;
    }

    *let_pos = (u8_t)letter_pos;

    return 1;
}

static u8_t lwip_ioctl_internal_SIOCSIFNAME(struct ifreq *ifr)
{
    struct netif *netif = NULL;
    u8_t letter_pos = 0;

    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else if (netif->link_layer_type == LOOPBACK_IF) {
        return EPERM;
    } else if ((netif->flags & IFF_UP) != 0) {
        return EBUSY;
    } else {
        if (strncmp(ifr->ifr_name, ifr->ifr_newname, IFNAMSIZ) == 0) {
            /* not change */
            return 0;
        }

        ifr->ifr_newname[IFNAMSIZ - 1] = '\0';
        if ((lwip_validate_ifname(ifr->ifr_newname, &letter_pos) == 0) || (strlen(ifr->ifr_newname) > (IFNAMSIZ - 1))) {
            return EINVAL;
        }

        if (strncpy_s(netif->full_name, sizeof(netif->full_name), ifr->ifr_newname, strlen(ifr->ifr_newname)) != EOK) {
            return EINVAL;
        }
    }

    return 0;
}

static u8_t lwip_ioctl_internal_SIOCGIFINDEX(struct ifreq *ifr)
{
    struct netif *netif = NULL;

    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else {
        ifr->ifr_ifindex = netif_get_index(netif);
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFMTU(struct ifreq *ifr)
{
    struct netif *netif = NULL;

    /* get netif hw addr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    } else {
        ifr->ifr_mtu = netif->mtu;
        return 0;
    }
}

static u8_t lwip_ioctl_internal_SIOCGIFBRDADDR(struct ifreq *ifr)
{
    struct netif *netif = NULL;
    struct sockaddr_in *sock_in = NULL;

    /* get netif subnet broadcast addr */
    netif = netif_find(ifr->ifr_name);
    if (netif == NULL) {
        return ENODEV;
    }
    if (ip4_addr_isany_val(*(ip_2_ip4(&netif->netmask)))) {
        return ENXIO;
    }
    sock_in = (struct sockaddr_in *)&ifr->ifr_addr;
    sock_in->sin_family = AF_INET;
    sock_in->sin_addr.s_addr = (ip_2_ip4(&((netif)->ip_addr))->addr | ~(ip_2_ip4(&netif->netmask)->addr));
    return 0;
}

static u8_t lwip_ioctl_impl(const struct lwip_sock *sock, long cmd, void *argp)
{
    u8_t err = 0;
    struct ifreq *ifr = (struct ifreq *)argp;
    u8_t is_ipv6 = 0;

    /* allow it only on IPv6 sockets... */
    is_ipv6 = NETCONNTYPE_ISIPV6((unsigned int)(sock->conn->type));

    switch ((u32_t)cmd) {
        case SIOCGIFCONF:
            if (is_ipv6 != 0) {
                err = EINVAL;
            } else {
                err = lwip_ioctl_internal_SIOCGIFCONF(ifr);
            }
            break;
        case SIOCGIFADDR:
            if (is_ipv6 != 0) {
                err = EINVAL;
            } else {
                err = lwip_ioctl_internal_SIOCGIFADDR(ifr);
            }
            break;
        case SIOCGIFNETMASK:
            if (is_ipv6 != 0) {
                err = EINVAL;
            } else {
                err = lwip_ioctl_internal_SIOCGIFNETMASK(ifr);
            }
            break;
        case SIOCGIFHWADDR:
            err = lwip_ioctl_internal_SIOCGIFHWADDR(ifr);
            break;
        case SIOCSIFFLAGS:
            err = lwip_ioctl_internal_SIOCSIFFLAGS(ifr);
            break;
        case SIOCGIFFLAGS:
            err = lwip_ioctl_internal_SIOCGIFFLAGS(ifr);
            break;
        case SIOCGIFNAME:
            err = lwip_ioctl_internal_SIOCGIFNAME(ifr);
            break;
        case SIOCSIFNAME:
            err = lwip_ioctl_internal_SIOCSIFNAME(ifr);
            break;
        case SIOCGIFINDEX:
            err = lwip_ioctl_internal_SIOCGIFINDEX(ifr);
            break;
        case SIOCGIFMTU:
            err = lwip_ioctl_internal_SIOCGIFMTU(ifr);
            break;
        case SIOCGIFBRDADDR:
            if (is_ipv6 != 0) {
                err = EINVAL;
            } else {
                err = lwip_ioctl_internal_SIOCGIFBRDADDR(ifr);
            }
            break;
        default:
            err = ENOSYS;
            LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(UNIMPL: 0x%lx)\n", cmd));
            break;
    }

    return err;
}

static err_t lwip_do_ioctl_impl(struct tcpip_api_call_data *call)
{
    struct lwip_ioctl_apimsg *msg = (struct lwip_ioctl_apimsg *)(void *)call;
    return lwip_ioctl_impl(msg->sock, msg->cmd, msg->argp);
}