/*
 *===========================================================================
 *                    ipnet_cmd_qc_parse_bit_string
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC Ip_bool
ipnet_cmd_qc_parse_bit_string(const char *bit_str, Ip_u32 *pattern, Ip_u8 *pattern_len)
{
    char *ctx;
    char *bit;
    char *dup_bit_str = ipcom_strdup(bit_str);

    if (dup_bit_str == IP_NULL)
    {
        ipcom_printf("Failed to duplicate string '%s'"IP_LF, bit_str);
        return IP_FALSE;
    }

    *pattern = 0;
    *pattern_len = 0;
    bit = ipcom_strtok_r(dup_bit_str, ",", &ctx);
    if (bit == IP_NULL)
    {
        ipcom_free(dup_bit_str);
        ipcom_printf("Invalid format of the pattern, must be a comma separated list of 0 and 1"IP_LF);
        return IP_FALSE;
    }

    while (bit != IP_NULL)
    {
        if (ipcom_atoi(bit))
            IP_BIT_SET(*pattern, 1 << *pattern_len);
        ++*pattern_len;
        bit = ipcom_strtok_r(IP_NULL, ",", &ctx);
    }

    ipcom_free(dup_bit_str);
    return IP_TRUE;
}
Esempio n. 2
0
/*
 *===========================================================================
 *                    ipnet_usr_sock_tcp_recv
 *===========================================================================
 * Description: Receives data from a TCP stream.
 * Parameters:  sock - The socket to receive data from.
 *              msg - Receive parameters.
 *              flags - IP_MSG_xxx flags.
 * Returns:     0 = a packet was successfully received, -1 = error.
 *
 */
IP_STATIC int
ipnet_usr_sock_tcp_recv(Ipnet_socket *sock, struct Ip_msghdr *msg, int flags)
{
    Ip_size_t i;
    int       r;
    int       bytes = 0;

    /* No support for ancillary data on TCP sockets */
    msg->msg_controllen = 0;

    if (msg->msg_name != IP_NULL)
    {
        Ip_socklen_t msg_namelen = msg->msg_namelen;

        r = ipnet_sys_getname(sock->ipcom.fd,
                              msg->msg_name,
                              &msg_namelen,
                              IP_FALSE);
        if (r < 0)
            return r;
        msg->msg_namelen = msg_namelen;
    }

    for (i = 0; i < msg->msg_iovlen; i++)
    {
        r = iptcp_usr_recv(sock,
                           msg->msg_iov[i].iov_base,
                           msg->msg_iov[i].iov_len,
                           flags);

        if (r == 0)
            /* EOF received */
            break;

        if (r < 0)
        {
            if (bytes > 0)
                break;
            IPCOM_WV_EVENT_2 (IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_WARNING,
                              1, 1, IPCOM_WV_NETDEVENT_WARNING, IPCOM_WV_NETD_RECV,
                              ipnet_usr_sock_tcp_recv, IPCOM_WV_NETD_BADSOCK,
                              IPCOM_WV_IPNET_SOCK_TCP_MODULE, IPCOM_WV_NETD_TCP4);
            IPNET_STATS(sock_recvmsg_tcp_err++);
            return r;
        }
        bytes += r;

        if ((Ip_size_t) r != msg->msg_iov[i].iov_len)
            /* Continue only if the current buffer was filled to its
               limit */
            break;
        if (IP_BIT_ISFALSE(flags, IP_MSG_WAITALL))
            /* We have some data, return the data we have so far if there
               isn't anything more to receive */
            IP_BIT_SET(flags, IP_MSG_DONTWAIT);
    }
    return bytes;
}
/*
 *===========================================================================
 *                    ipnet_loopback_raw_output
 *===========================================================================
 * Description: Output function for AF_PACKET socket datagrams.
 * Parameters:  netif - The interface the packet was sent to.
 *              to - The recipient of this packet.
 *              pkt - The packet to loopback. pkt->start is the offset to the
 *              IP header.
 * Returns:     0 = success, 0< = error code.
 *
 */
IP_STATIC int
ipnet_loopback_raw_output(struct Ipnet_netif_struct *netif,
                          IP_CONST struct Ip_sockaddr_ll *to,
                          Ipcom_pkt *pkt)
{
#ifdef IPCOM_USE_INET
    if (ip_ntohs(to->sll_protocol) == IP_IPPROTO_IPV4)
    {
        IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV4);
        return netif->link_ip4_output(netif, IP_NULL, pkt);
    }
#endif /* IPCOM_USE_INET */

#ifdef IPCOM_USE_INET6
    if (ip_ntohs(to->sll_protocol) == IP_IPPROTO_IPV6)
    {
        IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV6);
        return netif->link_ip6_output(netif, IP_NULL, pkt);
    }
#endif /* IPCOM_USE_INET6 */

    IP_PANIC2();
    return -IP_ERRNO_EINVAL;
}
Esempio n. 4
0
/*
 *===========================================================================
 *                    ipnet_sock_udp_init
 *===========================================================================
 * Description: The UDP transport layer initialization.
 * Parameters:  sock - The socket that is being created.
 *              user_context - unused by UDP.
 * Returns:     0 = success, <0 = error code
 *
 */
IP_STATIC int
ipnet_sock_udp_init(Ipnet_socket *sock, Ip_bool user_context)
{
    Ipnet_sock_udp_ops *ops = (Ipnet_sock_udp_ops *) sock->ops;

    (void) user_context;
    sock->proto      = IP_IPPROTO_UDP;
    sock->chk_offset = 6;
    if (ipcom_sysvar_get_as_int("ipnet.inet.UdpChecksum", 1))
        IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);

    if (ops->network_init != IP_NULL)
        return ops->network_init(sock, user_context);
    return 0;
}
/*
 *===========================================================================
 *                    ipnet_loopback_drv_output
 *===========================================================================
 * Description: Loops back the packet.
 * Parameters:  netif - The interface the packet was sent to.
 *              pkt - The packet to loopback. pkt->start is the offset to the
 *              IP header.
 * Returns:     0 = success, 0< = error code.
 *
 */
IP_STATIC int
ipnet_loopback_drv_output(Ipcom_netif *netif, Ipcom_pkt *pkt)
{
    int ret = 0;

    /* The ipcom_pkt_input_queue() frees the packet if it cannot be queued,
       add a reference so that packet is still allocated if that happens */
    IPCOM_PKT_ADD_REF(pkt);
    IPCOM_PKT_TRACE(pkt, IPCOM_PKT_ID_RX);
    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_LOOPED_BACK);
    ret = ipcom_pkt_input_queue(netif, pkt);
    if (IP_UNLIKELY(ret != -IP_ERRNO_EWOULDBLOCK))
    {
        ip_assert(IPCOM_PKT_HAS_MULTIPLE_OWNERS(pkt));
        ipcom_pkt_free(pkt);
    }
    return ret;
}
Esempio n. 6
0
/*
 *===========================================================================
 *                    ipnet_sock_tcp_init
 *===========================================================================
 * Description: The TCP transport layer initialization.
 * Parameters:
 *              user_context - IP_TRUE if this function is called in user process context
 *                             IP_FALSE if it called from stack context.
 * Returns:     0 = success, <0 = error code
 *
 */
IP_STATIC int
ipnet_sock_tcp_init(Ipnet_socket *sock, Ip_bool user_context)
{
    Ipnet_sock_tcp_ops *ops = (Ipnet_sock_tcp_ops *) sock->ops;

    sock->proto = IP_IPPROTO_TCP;
    if (user_context)
        /* Socket create in user-context, add an extra reference for the TCP module */
        ipcom_atomic_inc(&sock->ref_count);
    /* else: the TCP module will add an reference for the user when returning
       the socket in accept() */
    sock->chk_offset = 16;
    IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_ADDCHECKSUM);

    sock->tcb = ipnet_sock_alloc_private_data(sock, sizeof(Iptcp_tcb));
    iptcp_sock_init(sock);

    if (ops->network_init != IP_NULL)
        return ops->network_init(sock, user_context);
    return 0;
}
Esempio n. 7
0
/*
 *===========================================================================
 *                    ipnet_sock_udp_connect
 *===========================================================================
 * Description: The UDP transport layer connect.
 * Parameters:  sock - The socket to connect.
 *              to - The address to connect to.
 *              tolen - The size of the 'to' buffer.
 * Returns:     0 = success, <0 = error code
 *
 */
IP_STATIC int
ipnet_sock_udp_connect(Ipnet_socket *sock, IP_CONST struct Ip_sockaddr *to, Ip_socklen_t tolen)
{
    Ipnet_sock_udp_ops *ops = (Ipnet_sock_udp_ops *) sock->ops;
    int                 ret;

    ret = ops->network_connect(sock, to, tolen);
    if (ret < 0)
        return ret;

    if (ret == 1)
        /* Socket was unconnected */
        return 0;

    if (sock->dport == 0)
        /* Invalid port */
        return -IP_ERRNO_EINVAL;

    IP_BIT_SET(sock->flags, IPNET_SOCKET_FLAG_CONNECTED);
    return 0;
}
/*
 *===========================================================================
 *                    ipnet_sysctl_route_dump_cb
 *===========================================================================
 * Description: Checks if this entry should be added and adds it if it should.
 * Parameters:  rt - the route entry to add
 *              data - callback data.
 * Returns:     IP_FALSE (the entry should never be deleted).
 *
 */
IP_STATIC Ip_bool
ipnet_sysctl_route_dump_cb(Ipnet_route_entry *rt, Ipnet_sysctl_route_data *data)
{
    struct Ipnet_rt_msghdr *rt_msg;
    Ip_u16                  len;

    if (rt->next)
        (void)ipnet_sysctl_route_dump_cb(rt->next, data);

    if (rt->narrow)
        (void) ipnet_sysctl_route_dump_cb(rt->narrow, data);

    if ((rt->hdr.flags & data->flags) != data->flags)
        return IP_FALSE;

    /* Do not list the 255.255.255.255, 224.0.0.0/4 or IPv6 multicast routes. */
    if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_X_MCAST_RO | IPNET_RTF_X_BCAST_RO | IPNET_RTF_X_HIDDEN))
          return IP_FALSE;

    len = ipnet_sysctl_route_dump_elem_len(rt);
    data->bytes_written += len;

    if (data->bytes_written > data->buf_len)
    {
        /* Buffer to small */
        if (data->buf != IP_NULL)
            data->soerrno = -IP_ERRNO_ENOMEM;
        return IP_FALSE;
    }

    rt_msg = (struct Ipnet_rt_msghdr *) data->buf;
    data->buf = (Ip_u8 *) data->buf + sizeof(struct Ipnet_rt_msghdr);

    ipcom_memset(rt_msg, 0, sizeof(struct Ipnet_rt_msghdr));
    rt_msg->rtm_msglen  = len;
    rt_msg->rtm_version = IPNET_RTM_VERSION;
    rt_msg->rtm_type    = IPNET_RTM_GET;
    rt_msg->rtm_index   = rt->netif ? rt->netif->ipcom.ifindex : 0;
    rt_msg->rtm_table   = data->table;
    rt_msg->rtm_flags   = rt->hdr.flags;
    rt_msg->rtm_use     = rt->use;
    rt_msg->rtm_rmx     = rt->metrics;

    /* add destination address */
    IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_DST);
    data->buf = ipnet_sysctl_route_add_addr(data->buf, IPNET_ROUTE_GET_FAMILY(rt->head), rt->hdr.key);

    if (rt->gateway)
    {
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_GATEWAY);
        ipcom_memcpy(data->buf, rt->gateway, IPCOM_SA_LEN_GET(rt->gateway));
        data->buf = (Ip_u8 *) data->buf + IPCOM_SA_LEN_GET(rt->gateway);
    }
    else if (IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_CLONING))
    {
        struct Ip_sockaddr_dl  *dl = data->buf;
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_GATEWAY);
        ipcom_memset(data->buf, 0, sizeof(struct Ip_sockaddr_dl));
        IPCOM_SA_LEN_SET(dl, sizeof(struct Ip_sockaddr_dl));
        dl->sdl_family = IP_AF_LINK;
        ip_assert(rt->netif != IP_NULL);
        if (rt->netif != IP_NULL)
        {
            dl->sdl_index  = (Ip_u16)rt->netif->ipcom.ifindex;
            dl->sdl_type   = (Ip_u8)rt->netif->ipcom.type;
        }
        data->buf = (Ip_u8 *) data->buf + sizeof(struct Ip_sockaddr_dl);
    }

    if (rt->hdr.mask)
    {
        /* add destination mask */
        IP_BIT_SET(rt_msg->rtm_addrs, IPNET_RTA_NETMASK);
        data->buf = ipnet_sysctl_route_add_addr(data->buf, IPNET_ROUTE_GET_FAMILY(rt->head), rt->hdr.mask);
    }

    ip_assert((Ip_u16)((Ip_ptrdiff_t) data->buf - (Ip_ptrdiff_t) rt_msg) == len);

    return IP_FALSE;
}
Esempio n. 9
0
/*
 *===========================================================================
 *                      ipnet_if_mib_handler_ifTable
 *===========================================================================
 * Description: MIB handler for variables in ifTable
 * Parameters: See file 'ipsnmp.h'
 * Returns: IPSNMP_ERROR_XXX
 *
 */
IP_STATIC Ip_s32
ipnet_if_mib_handler_ifTable(Ip_s32 cmd,
                             char *id,
                             Ipsnmp_varbind *vb,
                             Ip_s32 magic,
                             struct Ipsnmp_node_object *nodeobj)
{
    Ip_s32          lid, ret = -1;
    Ipnet_netif    *best_netif;
    Ip_s32          ifAdminStatus = 0;
    char           *iid;
    char           *buf = ipcom_malloc(IPSNMP_CONFIG_MAX_OBJECT_ID);
    char           *best = ipcom_malloc(IPSNMP_CONFIG_MAX_OBJECT_ID);
    struct Ip_ifreq ifreq;
    Ip_fd           fd;

    if (buf == IP_NULL || best == IP_NULL)
    {
        ret = IPSNMP_ERROR_GENERROR;
        goto exit;
    }

    lid = ipsnmp_util_last_subid(nodeobj->id);
    ip_assert(lid >= 1 && lid <= 20);
    ip_assert(lid != 12 && lid != 18);
    best_netif = ipnet_if_mib_table_search_ifTable(id, buf, best, cmd, &ret);
    if (best_netif == IP_NULL)
        goto exit;

    if (cmd == IPSNMP_MIB_COMMAND_GET || cmd == IPSNMP_MIB_COMMAND_NEXT)
    {
        iid = ipsnmp_create_iid_direct(nodeobj->id, best);
        if (iid == IP_NULL)
        {
            ret = IPSNMP_ERROR_GENERROR;
            goto exit;
        }

        switch(lid)
        {
            case 1: /* ifIndex */
                ret = ipsnmp_util_put_integer(magic, iid, best_netif->ipcom.ifindex);
                break;
            case 2: /* ifDescr */
                ret = ipsnmp_util_put_octetstring(magic, iid, (Ip_u8 *)best_netif->ipcom.name, ipcom_strlen(best_netif->ipcom.name));
                break;
            case 3: /* ifType */
                ret = ipsnmp_util_put_integer(magic, iid, best_netif->ipcom.type);
                break;
            case 4: /* ifMtu */
                ret = ipsnmp_util_put_integer(magic, iid, best_netif->ipcom.mtu);
                break;
            case 5: /* ifSpeed */
                if (best_netif->ipcom.type == IP_IFT_PPP)
                    ret = ipsnmp_util_put_gauge32(magic, iid, IPCOM_DRV_PPP_BAUDRATE);
                else
                    ret = ipsnmp_util_put_gauge32(magic, iid, 100000000);
                break;
            case 6: /* ifPhysAddr */
                ret = ipsnmp_util_put_octetstring(magic, iid, best_netif->ipcom.link_addr, best_netif->ipcom.link_addr_size);
                break;
            case 7: /* ifAdminStatus */
                if (IP_BIT_ISSET(best_netif->ipcom.flags, IP_IFF_UP))
                    ret = ipsnmp_util_put_integer(magic, iid, 1);
                else
                    ret = ipsnmp_util_put_integer(magic, iid, 2);
                break;
            case 8: /* ifOperStatus */
                if (IP_BIT_ISSET(best_netif->ipcom.flags, IP_IFF_UP))
                    ret = ipsnmp_util_put_integer(magic, iid, 1);
                else
                    ret = ipsnmp_util_put_integer(magic, iid, 2);
                break;
            case 9: /* ifLastChange */
                ret = ipsnmp_util_put_timeticks(magic, iid, best_netif->ipcom.mib2.ifLastChange);
                break;
            case 10: /* ifInOctets */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifInOctets);
                break;
            case 11: /* ifInUcastPkts */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifInUcastPkts);
                break;
            case 13: /* ifInDiscards */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifInDiscards);
                break;
            case 14: /* ifInErrors */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifInErrors);
                break;
            case 15: /* ifInUnknownProtos */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifInUnknownProtos);
                break;
            case 16: /* ifOutOctets */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifOutOctets);
                break;
            case 17: /* ifOutUcastPkts */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifOutUcastPkts);
                break;
            case 19: /* ifOutDiscards */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifOutDiscards);
                break;
            case 20: /* ifOutErrors */
                ret = ipsnmp_util_put_counter32(magic, iid, best_netif->ipcom.mib2.ifOutErrors);
                break;
            default:
                IP_PANIC();
                ret = IPSNMP_ERROR_GENERROR;
                break;
        }

        ipcom_free(iid);
    }

    if (cmd == IPSNMP_MIB_COMMAND_TEST || cmd == IPSNMP_MIB_COMMAND_SET)
    {
        switch(lid)
        {
            case 7: /* ifAdminStatus */
                ret = ipsnmp_util_get_integer(vb, &ifAdminStatus);
                if (ret == IPSNMP_ERROR_NOERROR)
                {
                    if (ifAdminStatus != 1 && ifAdminStatus != 2)
                    {
                        ret = IPSNMP_ERROR_WRONGVALUE;
                    }
                }

                if (ret == IPSNMP_ERROR_NOERROR && cmd == IPSNMP_MIB_COMMAND_SET)
                {
                    fd = ipnet_do_socket(IP_AF_INET, IP_SOCK_DGRAM, IP_IPPROTO_UDP, IP_FALSE);
                    if(fd != IP_INVALID_SOCKET)
                    {
                        /* Change interface status */
                        ipcom_memset(&ifreq, 0, sizeof(struct Ip_ifreq));
                        ipcom_strcpy(ifreq.ifr_name, best_netif->ipcom.name);
                        if (ipnet_sys_socketioctl(fd, IP_SIOCGIFFLAGS, &ifreq) < 0)
                        {
                            IPCOM_LOG1(ERR, "Failed to get interface flags: %s", ipcom_strerror(ipcom_errno));
                            ret = IPSNMP_ERROR_GENERROR;
                        }
                        if (ifAdminStatus == 1)
                            IP_BIT_SET(ifreq.ip_ifr_flags, IP_IFF_UP);
                        else
                            IP_BIT_CLR(ifreq.ip_ifr_flags, IP_IFF_UP);
                        if (ipnet_sys_socketioctl(fd, IP_SIOCSIFFLAGS, &ifreq) < 0)
                        {
                            IPCOM_LOG1(ERR, "Failed to set interface flags: %s", ipcom_strerror(ipcom_errno));
                            ret = IPSNMP_ERROR_GENERROR;
                        }
                        ipnet_sys_socketclose(fd);
                    }
                    else
                    {
                        IPCOM_LOG0(ERR, "Failed to create socket for ioctl call");
                        ret = IPSNMP_ERROR_GENERROR;
                    }
                }
                break;
            default:
                IP_PANIC();
                ret = IPSNMP_ERROR_GENERROR;
                break;
        }
    }

exit:
    if (buf != IP_NULL)
        ipcom_free(buf);
    if (best != IP_NULL)
        ipcom_free(best);
    return ret;
}
Esempio n. 10
0
/*
 *===========================================================================
 *                    ipnet_rtnetlink_route_newroute_family
 *===========================================================================
 * Description: Add a new rotue via NETLINK.
 * Parameters:  data - Message payload
 *              len  - Length of message.
 *              nlh  - NETLINK message header
 *              arg  - Reference to route attributes.
 *
 * Returns:     0 - Success
 *             <0 - Failure
 *
 */
IP_GLOBAL int
ipnet_rtnetlink_route_newroute_family(Ipnet_netlink_mem_t   *mem,
                                      struct Ip_nlmsghdr    *nlh,
                                      struct Ip_rtattr      **rta,
                                      int                   family)
{
    Ipnet_netif                   *netif = IP_NULL;
    Ipnet_ppp_peer                *p = IP_NULL;
    int                           ret  = -1;
    Ip_u16                        vr;
    Ip_u32                        table;
    Ip_bool                       is_multipath_route = IP_FALSE;
    Ip_ssize_t                    mpa_size = 0;
    struct Ip_rtnexthop           *nh = IP_NULL;
    struct Ipnet_rt_metrics       metrics;
    struct Ip_sockaddr            *gw   = IP_NULL;
    void                          *mask = IP_NULL;
    struct Ip_sockaddr_dl         gw_dl;
    struct Ipnet_route_add_param  param;
    struct Ip_rtmsg               *rtm   = IP_NLMSG_DATA(nlh);

    union Ip_sockaddr_union       ugw;
    Ipnet_rtnetlink_key_t         ukey;

#ifdef IPMPLS
    struct Ip_sockaddr_mpls       gw_mpls;
#endif /* IPMPLS */
#ifdef IPNET_USE_ROUTE_COOKIES
    Ipnet_rt_cookie               cookie;
#endif /* IPNET_USE_ROUTE_COOKIES */

    table = IPCOM_ROUTE_TABLE_GET(rtm->rtm_table);

    /* Prepare route parameters */
    ipcom_memset(&param, 0, sizeof(param));
    ipcom_memset(&metrics,0,sizeof(metrics));
    param.flags = IPNET_RTF_STATIC | IPNET_RTF_UP | IPNET_RTF_DONE;

#ifdef IPNET_USE_ROUTE_TABLE_NAMES
    if (rta[IP_RTA_TABLE_NAME -1])
    {
        if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_RTA_TABLE_NAME - 1]),
                                               &vr,
                                               &table) < 0)
            return -IP_ERRNO_ESRCH;
    }
    else
#endif
    {
        if (mem->vr == IPCOM_VR_ANY)
            return -IP_ERRNO_EINVAL;

        vr = ipnet_rtnetlink_vr(rta[IP_RTA_VR - 1], mem->vr);

        if (rta[IP_RTA_TABLE - 1])
            table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_TABLE - 1]));
    }

    if (rta[IP_RTA_OIF -1])
    {
        int ifindex = 0;

        if (rta[IP_RTA_OIF-1]->rta_len != IP_RTA_LENGTH(sizeof(int)))
			return -IP_ERRNO_EINVAL;

        ifindex = IP_GET_32ON8(IP_RTA_DATA(rta[IP_RTA_OIF-1]));
        netif = ipnet_if_indextonetif(vr, ifindex);
        if (netif)
            p = netif->private_data;
    }

    if (rta[IP_RTA_MULTIPATH -1])
    {
        is_multipath_route = IP_TRUE;
        nh = IP_RTA_DATA(rta[IP_RTA_MULTIPATH-1]);
        mpa_size = IP_RTA_PAYLOAD(rta[IP_RTA_MULTIPATH-1]);

        if ((nh == IP_NULL) || (mpa_size == 0))
            return -IP_ERRNO_EINVAL;
    }

    if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, &mask, rtm->rtm_dst_len, rta[IP_RTA_DST-1])) < 0)
        return ret;

    if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, rta[IP_RTA_GATEWAY-1])) < 0)
        return ret;

#ifdef IPCOM_USE_INET
    if (family == IP_AF_INET)
    {
        if (gw != IP_NULL)
        {
            /* Check if it is a pure gateway or not */
            if (p == IP_NULL || p->peer4.s_addr != ugw.sin.sin_addr.s_addr)
                param.flags |= IPNET_RTF_GATEWAY;
        }
    }
    else
#endif
#ifdef IPCOM_USE_INET6
    if (family == IP_AF_INET6)
    {
        if (gw != IP_NULL)
        {
            /* Check if it is a pure gateway or not */
            if (p == IP_NULL || !IP_IN6_ARE_ADDR_EQUAL(&ugw.sin6.sin6_addr, &p->peer6))
                param.flags |= IPNET_RTF_GATEWAY;
        }
    }
    else
#endif
    {
        return -IP_ERRNO_EAFNOSUPPORT;
    }

    if (!mask)
        param.flags |= IPNET_RTF_HOST;

    if (rtm->rtm_type == IP_RTN_PROXY)
    {
        /* This is a proxy arp network route */
        IP_BIT_CLR(param.flags, IPNET_RTF_CLONING);
        IP_BIT_SET(param.flags, IPNET_RTF_PROTO2);

        if (rta[IP_RTA_PROXY_ARP_LLADDR - 1])
        {
            /* This is a network proxy arp with specific lladdr */
            if (netif == IP_NULL)
               return -IP_ERRNO_EINVAL;

            ipcom_memset(&gw_dl, 0, sizeof(struct Ip_sockaddr_dl));

            IPCOM_SA_LEN_SET(&gw_dl, sizeof(struct Ip_sockaddr_dl));
            gw_dl.sdl_family = IP_AF_LINK;
            gw_dl.sdl_index  = (Ip_u16)netif->ipcom.ifindex;
            gw_dl.sdl_alen   = (Ip_u8) netif->ipcom.link_addr_size;
            gw_dl.sdl_type   = (Ip_u8) netif->ipcom.type;

            ipcom_memcpy(IP_SOCKADDR_DL_LLADDR(&gw_dl),
                         IP_RTA_DATA(rta[IP_RTA_PROXY_ARP_LLADDR - 1]),
                         IPNET_ETH_ADDR_SIZE);

            gw = (struct Ip_sockaddr *) &gw_dl;
            IP_BIT_SET(param.flags, IPNET_RTF_LLINFO);
        }
    }
    else if (rtm->rtm_type == IP_RTN_PROHIBIT)
        IP_BIT_SET(param.flags, IPNET_RTF_REJECT);
    else if (rtm->rtm_type == IP_RTN_THROW)
        IP_BIT_SET(param.flags, IPNET_RTF_SKIP);
    else if (rtm->rtm_type == IP_RTN_UNREACHABLE)
    {
        if (netif == IP_NULL)
            netif = ipnet_loopback_get_netif(vr);
        IP_BIT_CLR(param.flags, IPNET_RTF_UP);
    }
    else if (rtm->rtm_type == IP_RTN_CLONE)
        IP_BIT_SET(param.flags, IPNET_RTF_CLONING);
    else if (rtm->rtm_type == IP_RTN_BLACKHOLE)
        IP_BIT_SET(param.flags, IPNET_RTF_BLACKHOLE);

#ifdef IPMPLS
    /* Check for IPNET MPLS pseudo-wire route */
    if (rta[IP_RTA_NH_PROTO-1])
    {
        Ip_u32 nh_type;

        if (rta[IP_RTA_NH_PROTO-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        if (rta[IP_RTA_NH_PROTO_DATA-1]->rta_len != IP_RTA_LENGTH(sizeof(Ip_u32)))
            return -IP_ERRNO_EINVAL;

        ipcom_memcpy(&nh_type, IP_RTA_DATA(rta[IP_RTA_NH_PROTO-1]), sizeof(Ip_u32));

        if (nh_type !=  IPNET_ETH_P_MPLS_UNICAST)
            return -IP_ERRNO_EINVAL;

        ipcom_memset(&gw_mpls, 0, sizeof(gw_mpls));
        gw_mpls.smpls_family = IP_AF_MPLS;
        IPCOM_SA_LEN_SET(&gw_mpls, sizeof (struct Ip_sockaddr_mpls));

        ipcom_memcpy(&gw_mpls.smpls_key,
                     IP_RTA_DATA(rta[IP_RTA_NH_PROTO_DATA-1]),
                     sizeof(Ip_u32));

        IP_BIT_CLR(param.flags,IPNET_RTF_GATEWAY);
        gw = (struct Ip_sockaddr *)&gw_mpls;
    }
    else
    {
        /* Check if cloning flag shall be set */
        if (IP_BIT_ISFALSE(param.flags, IPNET_RTF_HOST | IPNET_RTF_GATEWAY | IPNET_RTF_REJECT | IPNET_RTF_BLACKHOLE))
            IP_BIT_SET(param.flags, IPNET_RTF_CLONING);
    }
#endif

    if (rta[IP_RTA_METRICS-1])
    {
        int rlen = (int)IP_RTA_PAYLOAD(rta[IP_RTA_METRICS-1]);
        Ip_u32 dummy = 0;
        struct Ip_rtattr *rtax = (struct Ip_rtattr*)IP_RTA_DATA(rta[IP_RTA_METRICS-1]);

        metrics.rmx_expire = IPCOM_ADDR_INFINITE;
        param.metrics      = &metrics;

        for(;rlen > 0; rlen -= (int)rtax->rta_len,rtax = IP_RTA_NEXT(rtax,dummy))
        {
            switch (rtax->rta_type)
            {
            case IP_RTAX_MTU:
                ipcom_memcpy(&metrics.rmx_mtu, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            case IP_RTAX_RTT:
                ipcom_memcpy(&metrics.rmx_rtt, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            case IP_RTAX_RTTVAR:
                ipcom_memcpy(&metrics.rmx_rttvar, IP_RTA_DATA(rtax), sizeof(Ip_u32));
                break;
            }
        }
    }

#ifdef IPNET_USE_ROUTE_COOKIES
    if (rta[IP_RTA_COOKIE-1])
    {
        if (IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1]) > sizeof(cookie))
            return -IP_ERRNO_EINVAL;

        ipcom_memset(&cookie, 0, sizeof(cookie));
        ipcom_memcpy(&cookie,
                     IP_RTA_DATA(rta[IP_RTA_COOKIE-1]),
                     IP_RTA_PAYLOAD(rta[IP_RTA_COOKIE-1]));

        param.cookie = &cookie;
    }
#endif /* IPNET_USE_ROUTE_COOKIES */

    while (1)
    {
        if (is_multipath_route && (gw == IP_NULL))
        {
            netif                = ipnet_if_indextonetif(vr, nh->rtnh_ifindex);
            metrics.rmx_hopcount = nh->rtnh_hops;
            metrics.rmx_expire   = IPCOM_ADDR_INFINITE;
            param.metrics        = &metrics;

            if (nh->rtnh_len > sizeof(struct Ip_rtnexthop))
            {
                /* Multihop route has gateway */
                IP_BIT_CLR(param.flags, IPNET_RTF_CLONING);
                IP_BIT_SET(param.flags, IPNET_RTF_GATEWAY);

                if ((ret = ipnet_rtnetlink_route_gw_setup(family, netif? netif->ipcom.ifindex : 0, &ugw, &gw, IP_RTNH_DATA(nh))) < 0)
                    return ret;
            }
            mpa_size -= IP_RTNH_ALIGN(nh->rtnh_len);
            if (mpa_size > 0)
                nh = IP_RTNH_NEXT(nh);
        }

        param.domain       = family;
        param.vr           = vr;
        param.table        = table;
        param.netif        = netif;
        param.key          = &ukey;
        param.netmask      = mask;
        param.gateway      = gw;
        param.pid          = mem->pid;
        param.seq          = nlh->nlmsg_seq;
        param.no_ref_count = IP_TRUE;

        /* Try to add the route */
        if (netif)
            IPNET_IF_LOCK(netif);

        ret = ipnet_route_add(&param);

        if (netif != IP_NULL)
            IPNET_IF_UNLOCK(netif);

        if (is_multipath_route)
        {
            gw = IP_NULL;

            if (ret < 0 && ret != -IP_ERRNO_EEXIST)
                goto rollback;

            if (mpa_size == 0)
            {
                ret = 0;
                break;
            }
            else if (mpa_size < 0)
            {
                /* Malformed packet */
                ret = -IP_ERRNO_EINVAL;
                goto rollback;
            }
        }
        else
            break;
    }

    return ret;

rollback:
    (void)ipnet_rtnetlink_route_delroute_family(mem, nlh, rta, family);
    return ret;
}
/*
 *===========================================================================
 *                    ipnet_gre_output_append
 *===========================================================================
 * Description: Handler for packets to send.
 * Parameters:  pkt - Packet to send and append GRE header to.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_gre_output_append(Ipnet_netif *netif, struct Ip_ip_tunnel_param *tunnel_param, Ipcom_pkt *pkt)
{
    Ipnet_pkt_gre          *gre_hdr;
    Ipnet_pkt_gre_opt_csum *csum_opt = IP_NULL;
#ifdef IPNET_USE_RFC2890
    Ip_u32                 *key_opt = IP_NULL;
    Ip_u32                 *key_opt_info;
    Ip_u32                 *seqnum_opt = IP_NULL;
    Ipnet_gre_t            *gre = netif->ipcom.pdrv;
#endif

    ip_assert(pkt->start >= (IPNET_GRE_MAX_HDR_SIZE + IPNET_IP_HDR_SIZE));

#ifdef IPNET_USE_RFC2890
    if (IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK1))
    {
        pkt->start -= IPNET_GRE_OPT_SEQNUM_SIZE;
        seqnum_opt = (Ip_u32 *) &pkt->data[pkt->start];
        IP_SET_HTONL(seqnum_opt, gre->send_seqnum);
        ++gre->send_seqnum;
    }

    if ((key_opt_info = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_GRE_KEY_OUTPUT)) != IP_NULL)
    {
        pkt->start -= IPNET_GRE_OPT_KEY_SIZE;
        key_opt = (Ip_u32 *) &pkt->data[pkt->start];
        IP_SET_HTONL(key_opt, *key_opt_info);
    }
#endif /* IPNET_USE_RFC2890 */

    if (IP_BIT_ISTRUE(tunnel_param->o_flags, IPCOM_TUNNEL_FLAG_GRE_CHECKSUM)
        || IP_BIT_ISSET(netif->ipcom.flags, IP_IFF_LINK2))
    {
        /* GRE output checksum enabled. */
        pkt->start -= IPNET_GRE_OPT_CSUM_SIZE;
        csum_opt = (Ipnet_pkt_gre_opt_csum *) &pkt->data[pkt->start];
        csum_opt->checksum  = 0;
        csum_opt->reserved1 = 0;
    }

    /* Add GRE header. */
    pkt->start -= IPNET_GRE_HDR_SIZE;
    gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
    gre_hdr->flags_reserved0_ver = 0;
    switch (pkt->data[pkt->ipstart] & 0xf0)
    {
#ifdef IPCOM_USE_INET
    case 0x40:
        gre_hdr->protocol_type = IPNET_GRE_PROTOCOL_TYPE_IPV4;
        break;
#endif
#ifdef IPCOM_USE_INET6
    case 0x60:
        gre_hdr->protocol_type = IPNET_GRE_PROTOCOL_TYPE_IPV6;
        break;
#endif
    default:
        return -IP_ERRNO_EPROTONOSUPPORT;
    }

#ifdef IPNET_USE_RFC2890
    if (key_opt != IP_NULL)
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_KEY);

    if (seqnum_opt != IP_NULL)
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_SEQNUM);
#endif /* IPNET_USE_RFC2890 */

    if (csum_opt != IP_NULL)
    {
        /* The whole GRE header is written, time to calculate the checksum */
        IP_BIT_SET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_CHECKSUM);
        csum_opt->checksum = ipcom_in_checksum(gre_hdr, pkt->end - pkt->start);
    }

    return 0;
}
/*
 *===========================================================================
 *                    ipnet_gre_input
 *===========================================================================
 * Description: Handler for received GRE packets.
 * Parameters:  pkt - Received GRE packet.
 * Returns:     0 = success, <0 = error code.
 *
 */
IP_GLOBAL int
ipnet_gre_input(Ipnet_netif *netif, Ipcom_pkt *pkt)
{
    Ipnet_pkt_gre *gre_hdr;
    int            gre_hdr_start = pkt->start;

    IP_BIT_CLR(pkt->flags, IPCOM_PKT_FLAG_IPV4 | IPCOM_PKT_FLAG_IPV6);
    gre_hdr = (Ipnet_pkt_gre *)&pkt->data[pkt->start];
    pkt->start += IPNET_GRE_HDR_SIZE;

    /* First 16-bits non-null, verify bits and version. */
    if (gre_hdr->flags_reserved0_ver)
    {
        /* RFC 2784: 2.3. Reserved0 (bits 1-12)
           A receiver MUST discard a packet where any of bits 1-5 are non-zero,
           unless that receiver implements RFC 1701. Bits 6-12 are reserved for
           future use. These bits MUST be sent as zero and MUST be ignored on
           receipt.

           RFC 2890: 2. Extensions to GRE Header
           Key present (bit 2)
           Sequence Number Present (bit 3)
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_MUSTBE0))
            goto cleanup;

        /* RFC 2784: 2.3.1. Version Number (bits 13-15)
           The Version Number field MUST contain the value zero.
        */
        if (IP_BIT_ISSET(gre_hdr->flags_reserved0_ver, IPNET_GRE_FLAG_VERSION))
            goto cleanup;

        /* RFC 2784: 2.5. Checksum (2 octets)
           The Checksum field contains the IP (one's complement) checksum sum of
           the all the 16 bit words in the GRE header and the payload packet.
           For purposes of computing the checksum, the value of the checksum
           field is zero. This field is present only if the Checksum Present bit
           is set to one.
        */
        if (IPNET_GRE_CHECKSUM_PRESENT(gre_hdr))
        {
            pkt->start += IPNET_GRE_OPT_CSUM_SIZE;

            /* IP checksum must be ok. */
            if (ipcom_in_checksum(&gre_hdr->flags_reserved0_ver, pkt->end - gre_hdr_start) != 0)
                goto cleanup;
        }

#ifdef IPNET_USE_RFC2890
        /* RFC 2890: 2.1. Key Field (4 octets)
           The Key field contains a four octet number which was inserted by the
           encapsulator. The actual method by which this Key is obtained is
           beyond the scope of the document. The Key field is intended to be
           used for identifying an individual traffic flow within a tunnel. For
           example, packets may need to be routed based on context information
           not present in the encapsulated data.  The Key field provides this
           context and defines a logical traffic flow between encapsulator and
           decapsulator.  Packets belonging to a traffic flow are encapsulated
           using the same Key value and the decapsulating tunnel endpoint
           identifies packets belonging to a traffic flow based on the Key Field
           value.
        */
        if (IPNET_GRE_KEY_PRESENT(gre_hdr))
        {
            Ip_u32 key;

            key = IP_GET_NTOHL(&pkt->data[pkt->start]);
            IPNET_GRE_PKT_SET_KEY(pkt, key);
            pkt->start += IPNET_GRE_OPT_KEY_SIZE;
        }

        /* RFC 2890: 2.2. Sequence Number (4 octets)
           The Sequence Number field is a four byte field and is inserted by the
           encapsulator when Sequence Number Present Bit is set. The Sequence
           Number MUST be used by the receiver to establish the order in which
           packets have been transmitted from the encapsulator to the receiver.
           The intended use of the Sequence Field is to provide unreliable but
           in-order delivery. If the Key present bit (bit 2) is set, the
           sequence number is specific to the traffic flow identified by the Key
           field. Note that packets without the sequence bit set can be
           interleaved with packets with the sequence bit set.
        */
        if (IPNET_GRE_SEQNUM_PRESENT(gre_hdr))
        {
            Ipnet_gre_t *gre = netif->ipcom.pdrv;
            Ip_u32       seqnum;

            seqnum = IP_GET_NTOHL(&pkt->data[pkt->start]);
            if (seqnum == gre->recv_seqnum)
                /* In order segment */
                ++gre->recv_seqnum;
            else if (IPCOM_IS_LT(seqnum, gre->recv_seqnum))
                /* Out of order */
                goto cleanup;
            else
            {
                Ip_u32 outoforder_timer;
                int max_perflow_buffer;

                max_perflow_buffer = ipcom_sysvar_get_as_int0("ipnet.gre.MAX_PERFLOW_BUFFER", 3);
                outoforder_timer = ipcom_sysvar_get_as_int0("ipnet.gre.OUTOFORDER_TIMER", 1000);

                if (max_perflow_buffer <= 0)
                    /* No buffering allowed */
                    goto cleanup;

                while (ipcom_pqueue_size(gre->reassembly_queue) >= max_perflow_buffer)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    /* Call the timeout handler to force the oldest packet(s)
                       to be inputted to the stack and the receive sequence
                       number to advanced */
                    ipnet_gre_seq_tmo(netif);
                }

                if (gre_hdr->protocol_type == ip_htons(0x0800))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV4);
                else if (gre_hdr->protocol_type == ip_htons(0x86DD))
                    IP_BIT_SET(pkt->flags, IPCOM_PKT_FLAG_IPV6);

                IPNET_PKT_SET_TIMEOUT_ABS(pkt, ipnet->msec_now + outoforder_timer);
                if (gre->reassembly_tmo == IP_NULL)
                    if (ipnet_timeout_schedule(outoforder_timer,
                                               (Ipnet_timeout_handler) ipnet_gre_seq_tmo,
                                               netif,
                                               &gre->reassembly_tmo) < 0)
                        goto cleanup;
                if (ipcom_pqueue_insert(gre->reassembly_queue, pkt) != IPCOM_SUCCESS)
                {
                    ipnet_timeout_cancel(gre->reassembly_tmo);
                    goto cleanup;
                }
                return 0;
            }
            pkt->start += IPNET_GRE_OPT_SEQNUM_SIZE;
        }
#endif /* IPNET_USE_RFC2890 */
    }

    /**/
    if (netif->ipcom.link_tap)
        /* start is PAYLOAD == the inner IP header; ipstart points to the PREVIOUS
         * IP header */
        netif->ipcom.link_tap(&netif->ipcom, pkt, IP_PACKET_HOST, gre_hdr->protocol_type, pkt->start, pkt->start);

#ifdef IPCOM_USE_INET
    /* IPv4 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x0800))
        return ipnet_ip4_input(netif, pkt);
#endif

#ifdef IPCOM_USE_INET6
    /* IPv6 in GRE. */
    if (gre_hdr->protocol_type == ip_htons(0x86DD))
        return ipnet_ip6_input(netif, pkt);
#endif

    /* Unsupported Protocol Type, drop. */

 cleanup:
    ipcom_pkt_free(pkt);
    return -IP_ERRNO_EINVAL;
}
/*
 *===========================================================================
 *                    ipcom_drv_ppp_ioctl
 *===========================================================================
 * Description: IO control.
 * Parameters:  netif - A network interface.
 *              command - The control task to perform.
 *              data - Data associated with the specified command.
 * Returns:     0 = success, <0 = error code
 *
 */
IP_STATIC int
ipcom_drv_ppp_ioctl(Ipcom_netif *netif, Ip_u32 command, void *data)
{
    Ipcom_pdrv_ppp      *pdrv;
    Ipcom_drv_ppp_link  *plink;
    struct Ip_pppreq    *pppreq;
    int ret;

    pdrv  = (Ipcom_pdrv_ppp *)netif->pdrv;
    plink = &pdrv->plink[netif->link_index];
    pppreq = data;

    switch (command)
    {
    case IP_SIOCXOPEN:
    case IP_SIOCXPPPSDRVUP:
        /* Create the driver. */
        ret = ipcom_drv_ppp_init(plink);
        if (ret != 0)
            return ret;

        /* Signal link up immediately. */
        netif->link_ioevent(netif, IP_EIOXUP, IP_NULL, 0);
        break;

    case IP_SIOCXCLOSE:
    case IP_SIOCXPPPSDRVDOWN:
        /* Kill the driver. */
        if (ipcom_drv_ppp_exit(plink) != 0)
            return -IP_ERRNO_EINVAL;

        /* Signal link down immediately. */
        netif->link_ioevent(netif, IP_EIOXDOWN, IP_NULL, 0);
        break;

    case IP_SIOCIFDESTROY:
        if (IP_BIT_ISSET(netif->flags, IP_IFF_RUNNING))
            return -IP_ERRNO_EBUSY;
        ret = ipcom_if_detach(netif);
        if (ret != 0)
            return 0;
        if (plink->devname)
            ipcom_free(plink->devname);
        if (pdrv != IP_NULL)
            ipcom_free(pdrv);
        return ipcom_if_free(netif);

    case IP_SIOCXPPPGDRVCONF:
        pppreq->pppru.drvconf.drv_flags = 0; /* See ipcom_netif.h for IP_DRVPPP_FLAG_xxx options. */
        pppreq->pppru.drvconf.baudrate = plink->baudrate;
        pppreq->pppru.drvconf.rcv_accm = 0xffffffff;
        pppreq->pppru.drvconf.snd_accm = 0xffffffff;
        pppreq->pppru.drvconf.mru = 1500;
        pppreq->pppru.drvconf.mtu = 1500;
        break;

    case IP_SIOCXPPPSDRVBAUDRATE:
        /* Running, change the baudrate. */
        if (plink->fd != -1)
            if (ioctl(plink->fd, FIOBAUDRATE, pppreq->pppru.drv_baudrate) != OK)
                return -IP_ERRNO_EINVAL;

        /* Baudrate changed. */
        plink->baudrate = pppreq->pppru.drv_baudrate;
        break;

    case IP_SIOCXPPPSDRVWINCOMPAT:
        plink->wincompat = pppreq->pppru.drv_wincompat;
        break;

    case IP_SIOCXPROMISC:
        if (*(Ip_bool *) data)
            IP_BIT_SET(netif->flags, IP_IFF_PROMISC);
        else
            IP_BIT_CLR(netif->flags, IP_IFF_PROMISC);
        break;

    default:
        return -IP_ERRNO_EINVAL;
    }

    return 0;
}
/*
 *===========================================================================
 *                    ipnet_fragment_packet
 *===========================================================================
 * Description: Splits a packet that is to big to fit into the MTU of the
 *              network interface.
 * Parameters:  pkt - The packet to split. pkt->start is the offset to the
 *              extension header (or transport layer protocol) with the
 *              id of 'next_hdr'. This packet is released by this function.
 *              ip_hdr_size - The size of the IP header.
 *              other_hdr_size - The size of other headers that will be
 *              added due to fragmentation.
 *              mtu - The MTU of the network interface.
 *              has_more_fragments - function that returns IP_TRUE if the
 *              packet passed as an argument has more fragments. This
 *              parameter might be IP_NULL.
 * Returns:     A list of fragments or IP_NULL if not enough memory could
 *              be allocated to create all fragments.
 */
IP_GLOBAL Ipcom_pkt *
ipnet_fragment_packet(Ipcom_pkt *pkt,
                      int ip_hdr_size,
                      int other_hdr_size,
                      int mtu,
                      Ipnet_more_fragments_func more_fragments)
{
    Ipcom_pkt *frag_pkt_head;
    Ipcom_pkt *frag_pkt_tail;
    Ipcom_pkt *frag_pkt;
    int        frag_size;
    int        total_size;
    int        size_of_this_fragment;
    int        frag_offset;
    Ipnet_pkt_info_sock_snd_buf *ssb;

    total_size = pkt->end - pkt->start;
    frag_size = (mtu - ip_hdr_size - other_hdr_size) & 0xfff8;
    frag_pkt_head = IP_NULL;
    frag_pkt_tail = IP_NULL;
    frag_offset = 0;

    while (frag_offset < total_size)
    {
        if (total_size - frag_offset > frag_size)
            size_of_this_fragment = frag_size;
        else
            size_of_this_fragment = total_size - frag_offset;

        frag_pkt = ipcom_pkt_malloc(mtu, IP_FLAG_FC_STACKCONTEXT);
        if (frag_pkt == IP_NULL)
        {
            IPCOM_WV_MARKER_1 ( IPCOM_WV_NETD_IP4_DATAPATH_EVENT, IPCOM_WV_NETD_CRITICAL, 1, 1, IPCOM_WV_NETD_NOBUFS,
                                ipnet_fragment_packet, IPCOM_WV_IPNET_FRAG_MODULE, IPCOM_WV_NETD_IP_REASSEMBLE);
            IPNET_STATS(pkt_frag_nomem++);
            ipcom_pkt_free(pkt);
            if (frag_pkt_head != IP_NULL)
                ipcom_pkt_free(frag_pkt_head);
            return IP_NULL;
        }

#ifdef IPCOM_USE_MIB2
        if(other_hdr_size == 0)
        {
#ifdef IPCOM_USE_INET
            Ipnet_netif *netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);

            IPCOM_MIB2(ipFragCreates++);
            if (netif != IP_NULL)
            {
                IPCOM_MIB2_SYSWI_U32_ADD(v4, ipSystemStatsOutFragCreates, 1);
                IPCOM_MIB2_PERIF_U32_ADD(v4, ipIfStatsOutFragCreates, 1, netif);
            }
#endif
        }
        else
        {
#ifdef IPCOM_USE_INET6
            Ipnet_netif *netif = ipnet_if_indextonetif(pkt->vr_index, pkt->ifindex);

            IPCOM_MIB2_NETIF(ipv6IfStatsOutFragCreates++);
            if (netif != IP_NULL)
            {
                IPCOM_MIB2_SYSWI_U32_ADD(v6, ipSystemStatsOutFragCreates, 1);
                IPCOM_MIB2_PERIF_U32_ADD(v6, ipIfStatsOutFragCreates, 1, netif);
            }
#endif
        }
#endif /* IPCOM_USE_MIB2 */

        if (frag_pkt_head == IP_NULL)
            frag_pkt_head = frag_pkt;
        else
            frag_pkt_tail->next_fragment = frag_pkt;
        frag_pkt_tail = frag_pkt;

        IPCOM_PKT_TRACE(frag_pkt, IPCOM_PKT_ID_FRAG);
        frag_pkt->fd       = pkt->fd;
        frag_pkt->ifindex  = pkt->ifindex;
        frag_pkt->flags    = pkt->flags;
        frag_pkt->vr_index = pkt->vr_index;
        /* Copy IP packet payload */
        frag_pkt->start      = (frag_pkt->maxlen - size_of_this_fragment) & ~0x3;
        frag_pkt->end        = frag_pkt->start + size_of_this_fragment;
        ip_assert(frag_pkt->start < frag_pkt->maxlen);
        ipcom_memcpy(&frag_pkt->data[frag_pkt->start],
                     &pkt->data[pkt->start + frag_offset],
                     (Ip_size_t)size_of_this_fragment);

        frag_pkt->offset     = (Ip_u16) (frag_offset + pkt->offset);
        frag_offset += size_of_this_fragment;
        IP_BIT_SET(frag_pkt->flags, IPCOM_PKT_FLAG_MORE_FRAG);
    }

    if (IP_BIT_ISFALSE(pkt->flags, IPCOM_PKT_FLAG_FRAGMENT)
        || (more_fragments && IP_FALSE == more_fragments(pkt)))
        /* This is either an IP datagram sourced from this node or the
           last fragment of an IP datagram that has been forwarded by
           this node. */
        IP_BIT_CLR(frag_pkt_tail->flags, IPCOM_PKT_FLAG_MORE_FRAG);

    ip_assert(pkt->start + frag_offset == pkt->end);
    ip_assert(frag_pkt_head != IP_NULL);
    if (IP_NULL != (ssb = ipcom_pkt_get_info(pkt, IPNET_PKT_INFO_SOCK_SND_BUF)))
    {
        /* Transfer the socket send buffer allocation from the
           original packet to the last fragment to make sure that the
           send flow control is still driven by TX done events. */
        ipcom_pkt_set_info(frag_pkt_tail,
                           IPNET_PKT_INFO_SOCK_SND_BUF,
                           sizeof(Ipnet_pkt_info_sock_snd_buf),
                           ssb);
        frag_pkt_tail->dtor = pkt->dtor;
        pkt->dtor = IP_NULL;
    }

    /* Do not free original yet, since it might hold the
       callers signal buffer. Put it in the fragment tails
       next original instead and it will be freed when the
       last segment has been transmitted */
    if (frag_pkt_tail != IP_NULL)
        frag_pkt_tail->next_original = pkt;
    else
        ipcom_pkt_free(pkt);

    return frag_pkt_head;
}
/*
 *===========================================================================
 *                    ipnet_rtnetlink_neigh_newneigh_family
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_GLOBAL int
ipnet_rtnetlink_neigh_newneigh_family(Ipnet_netlink_mem_t  *mem,
                                      struct Ip_nlmsghdr   *nlmsg,
                                      struct Ip_rtattr     **rta,
                                      int                  family)
{
    Ipnet_netif                  *netif;
    Ipnet_route_entry            *rt;
    int                          nd_state;
    int                          ret = -IP_ERRNO_EAFNOSUPPORT;
    Ip_u16                       vr;
    Ip_u32                       table = IPCOM_ROUTE_TABLE_DEFAULT;
    struct Ip_sockaddr_dl        dl;
    struct Ip_ndmsg              *ndm = IP_NLMSG_DATA(nlmsg);
    void                         *link_addr = IP_NULL;
    Ipnet_rtnetlink_key_t        ukey;

     /* Destination address required */
    if (rta[IP_NDA_DST-1] == IP_NULL)
        return -IP_ERRNO_EINVAL;

#ifdef IPNET_USE_ROUTE_TABLE_NAMES
    if (rta[IP_NDA_TABLE_NAME -1])
    {
        /* Extract router and table from name */
        if (ipnet_route_vr_and_table_from_name(IP_RTA_DATA(rta[IP_NDA_TABLE_NAME - 1]),
                                               &vr,
                                               &table) < 0)
            return -IP_ERRNO_ESRCH;
    }
    else
#endif /* IPNET_USE_ROUTE_TABLE_NAMES */
    {
        if (mem->vr == IPCOM_VR_ANY)
            return -IP_ERRNO_EINVAL;

        vr = ipnet_rtnetlink_vr(rta[IP_NDA_VR - 1], mem->vr);

        if (rta[IP_NDA_TABLE - 1])
            table = IP_GET_32ON8(IP_RTA_DATA(rta[IP_NDA_TABLE - 1]));
    }

    netif = ipnet_if_indextonetif(vr, ndm->ndm_ifindex);

    if (netif && (netif->vr_index != vr))
         return -IP_ERRNO_EINVAL;

    if ((ret = ipnet_rtnetlink_route_key_setup(family, &ukey, netif? netif->ipcom.ifindex : 0, IP_NULL, -1, rta[IP_NDA_DST-1])) < 0)
        return ret;

    if (rta[IP_NDA_LLADDR-1])
        /* Neighbors link layer address */
        link_addr = IP_RTA_DATA(rta[IP_NDA_LLADDR-1]);
    else if (IP_BIT_ISSET(ndm->ndm_flags, IP_NTF_PROXY))
    {
        /* Proxy ARP entry. Use Link layer address of local netif */
        if (netif == IP_NULL)
            return -IP_ERRNO_EINVAL;

        link_addr = netif->ipcom.link_addr;
    }

    /* Check if neighbor route exists */
    ret = ipnet_route_lookup(family,
                             vr,
                             table,
                             IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE,
                             &ukey,
                             netif ? netif->ipcom.ifindex : 0,
                             netif ? netif->ipcom.ifindex : 0,
                             &rt);

    if ((ret == IPNET_ROUTE_PERFECT_MATCH)
        && (rt->netif == netif)
        && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_HOST)
        && IP_BIT_ISSET(rt->hdr.flags, IPNET_RTF_LLINFO))
    {
        if (rt->data == IP_NULL)
            return -IP_ERRNO_EINVAL;

        /* Get the neighbour state */
        nd_state = *(Ipnet_nd_state_t *)rt->data;

        if (((ndm->ndm_state == IP_NUD_INCOMPLETE)
             && (nd_state != IPNET_ND_UNINITIALIZED)
             && (nd_state != IPNET_ND_INCOMPLETE))
            || ((ndm->ndm_state == IP_NUD_DELAY)
                && (nd_state != IPNET_ND_STALE)))
        {
            return -IP_ERRNO_EINVAL;
        }
    }
    else
    {
        struct Ipnet_rt_metrics      metrics;
        struct Ipnet_route_add_param param;

        /* Create new neighbor entry */
        if (ndm->ndm_state == IP_NUD_DELAY)
            ndm->ndm_state = IP_NUD_INCOMPLETE;

        ipcom_memset(&param, 0, sizeof(struct Ipnet_route_add_param));
        ipcom_memset(&dl, 0, sizeof(struct Ip_sockaddr_dl));

        param.domain     = family;
        param.vr         = vr;
        param.table      = table;
        param.key        = &ukey;
        param.netif      = netif;
        param.flags      = IPNET_RTF_UP | IPNET_RTF_HOST | IPNET_RTF_LLINFO | IPNET_RTF_DONE;
        param.gateway    = (struct Ip_sockaddr*) &dl;

        if (ndm->ndm_state != IP_NUD_PERMANENT)
        {
            ipcom_memset(&metrics, 0, sizeof(metrics));
            /* The correct timeout will be set when the ND state is set,
               but must be != infinite for now */
            metrics.rmx_expire = 1;
            param.metrics = &metrics;
        }

        IPCOM_SA_LEN_SET(&dl, sizeof(struct Ip_sockaddr_dl));
        dl.sdl_family    = IP_AF_LINK;
        dl.sdl_index     = (Ip_u16)(netif ? netif->ipcom.ifindex : 0);
        dl.sdl_alen      = 6;
        dl.sdl_type      = IP_IFT_ETHER;

        if (link_addr)
            ipcom_memcpy(&dl.sdl_data[0], link_addr,dl.sdl_alen);

        if (netif)
            IPNET_IF_LOCK(netif);

        /* Add new neighbor */
        ret = ipnet_route_add(&param);

        if (netif)
            IPNET_IF_UNLOCK(netif);

        if (ret < 0)
            return ret;

        /* Get the new neighbor route entry */
        ret = ipnet_route_lookup(family,
                                 vr,
                                 table,
                                 IPNET_RTL_FLAG_LINK_LOCAL | IPNET_RTL_FLAG_DONTCLONE,
                                 &ukey,
                                 netif ? netif->ipcom.ifindex : 0,
                                 netif ? netif->ipcom.ifindex : 0,
                                 &rt);
        if (ret < 0)
            return ret;
    }

#ifdef IPNET_COMPENSATE_UNRELIABLE_NETLINK
    /* Set neighbor acknowledge bit */
    IP_BIT_SET(rt->hdr.flags, IPNET_RTF_X_NEIGH_ACK);
#endif

    if (IP_BIT_ISSET(ndm->ndm_flags,IP_NTF_PROXY))
        /* Proxy ARP entry */
        IP_BIT_SET(rt->hdr.flags, IPNET_RTF_PROTO2);

    if (IP_BIT_ISSET(ndm->ndm_state, IP_NUD_PERMANENT))
        ipnet_route_set_lifetime(rt, IPCOM_ADDR_INFINITE);

    nd_state = ipnet_rtnetlink_neigh_nud2nc(ndm->ndm_state);

#ifdef IPCOM_USE_INET
    if (family == IP_AF_INET)
        ret = ipnet_rtnetlink_ip4_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta);
    else
#endif
#ifdef IPCOM_USE_INET6
    if (family == IP_AF_INET6)
        ret = ipnet_rtnetlink_ip6_neigh_setup(&ukey, ndm, rt, nd_state, link_addr, rta);
    else
#endif
        return -IP_ERRNO_EAFNOSUPPORT;

    return ret;
}
/*
 *===========================================================================
 *                      ipnet_cmd_qc_parse_filter
 *===========================================================================
 * Description:
 * Parameters:
 * Returns:
 *
 */
IP_STATIC int
ipnet_cmd_qc_parse_filter(Ipnet_cmd_qc *p)
{
    int    ret = 0;
    Ip_u32 n;
    struct Ipnet_classifier_rule *fr = &p->p.filter.filter_rule;
    int    prefixlen;

    if (p->argc < 4)
    {
        ipcom_printf("No filter rules specified"IP_LF);
        return -1;
    }

    while (ret == 0 && p->argc >= 2)
    {
        const char *type = p->argv[0];
        const char *val = p->argv[1];

        if (ipcom_strcmp(type, "flowid") == 0)
            break;
        ipnet_cmd_qc_next_arg(p);
        ipnet_cmd_qc_next_arg(p);

        if (ipcom_strcmp("srcaddr", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_SADDR);
            ret = ipnet_cmd_qc_parse_addr(val,
                                          &fr->af,
                                          &fr->saddr,
                                          &prefixlen);
            fr->saddr_prefixlen = (Ip_u8) prefixlen;
            continue;
        }
        if (ipcom_strcmp("dstaddr", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_DADDR);
            ret = ipnet_cmd_qc_parse_addr(val,
                                          &fr->af,
                                          &fr->daddr,
                                          &prefixlen);
            fr->daddr_prefixlen = (Ip_u8) prefixlen;
            continue;
        }

        if (ipcom_strcmp("srcport", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_SPORT);
            ret = ipnet_cmd_qc_parse_port_range(val,
                                                &fr->sport_low,
                                                &fr->sport_high);
            continue;
        }
        if (ipcom_strcmp("dstport", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_DPORT);
            ret = ipnet_cmd_qc_parse_port_range(val,
                                                &fr->dport_low,
                                                &fr->dport_high);
            continue;
        }

        if (!ipnet_cmd_qc_parse_number(val, &n))
            return -1;

        if (ipcom_strcmp("proto", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_PROTO);
            fr->proto = (Ip_u8) n;
        }
        else if (ipcom_strcmp("tclass", type) == 0
                 || ipcom_strcmp("tos", type) == 0
                 || ipcom_strcmp("ds", type) == 0)
        {
            IP_BIT_SET(fr->mask, IPNET_CLS_RULE_DS);
            fr->ds = (Ip_u8) n;
        }
        else
            break;
    }

    if (ret)
        return ret;

    if (p->argc == 0
        || ipcom_strcmp(*p->argv, "flowid") != 0)
    {
        ipcom_printf("No 'flowid' specified"IP_LF);
        return -1;
    }

    ipnet_cmd_qc_next_arg(p);
    if (!ipnet_cmd_qc_parse_number(*p->argv, &n))
        return -1;
    p->p.filter.filter_child_queue_id = (int) n;
    ipnet_cmd_qc_next_arg(p);

    return 0;
}