예제 #1
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_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;
}