diff --git a/conf/confbase.Y b/conf/confbase.Y index 7e0537c5..7cc74cf3 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -83,7 +83,7 @@ CF_DECLS %type <time> expr_us time %type <a> ipa %type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa -%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_mpls_ +%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_mreq4_ net_mreq6_ net_mreq_ net_mgrp4_ net_mgrp6_ net_mgrp_ net_mpls_ %type <mls> label_stack_start label_stack %type <t> text opttext @@ -234,6 +234,30 @@ net_roa6_: net_ip6_ MAX NUM AS NUM cf_error("Invalid max prefix length %u", $3); }; +net_mreq4_: MREQ4 '(' '*' ',' IP4 ')' +{ + $$ = cfg_alloc(sizeof(net_addr_mreq4)); + net_fill_mreq4($$, $5, 0); // XXXX +} + +net_mreq6_: MREQ6 '(' '*' ',' IP6 ')' +{ + $$ = cfg_alloc(sizeof(net_addr_mreq6)); + net_fill_mreq6($$, $5, 0); // XXXX +} + +net_mgrp4_: MGRP4 '(' '*' ',' IP4 ')' +{ + $$ = cfg_alloc(sizeof(net_addr_mgrp4)); + net_fill_mgrp4($$, $5); +} + +net_mgrp6_: MGRP6 '(' '*' ',' IP6 ')' +{ + $$ = cfg_alloc(sizeof(net_addr_mgrp6)); + net_fill_mgrp6($$, $5); +} + net_mpls_: MPLS NUM { $$ = cfg_alloc(sizeof(net_addr_roa6)); @@ -243,12 +267,16 @@ net_mpls_: MPLS NUM net_ip_: net_ip4_ | net_ip6_ ; net_vpn_: net_vpn4_ | net_vpn6_ ; net_roa_: net_roa4_ | net_roa6_ ; +net_mreq_: net_mreq4_ | net_mreq6_ ; +net_mgrp_: net_mgrp4_ | net_mgrp6_ ; net_: net_ip_ { $$ = cfg_alloc($1.length); net_copy($$, &($1)); } | net_vpn_ | net_roa_ | net_flow_ + | net_mreq_ + | net_mgrp_ | net_mpls_ ; diff --git a/lib/birdlib.h b/lib/birdlib.h index 428b3209..601cae58 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -40,6 +40,9 @@ struct align_probe { char x; long int y; }; #define CALL(fn, args...) ({ if (fn) fn(args); }) #define ADVANCE(w, r, l) ({ r -= l; w += l; }) +#define WALK_ARRAY(v,l,n) \ + for (typeof(*(v)) *_n = (v), n; _n < ((v) + (l)) && (n = *_n, 1); _n++) + static inline int uint_cmp(uint i1, uint i2) { return (int)(i1 > i2) - (int)(i1 < i2); } diff --git a/lib/net.c b/lib/net.c index 01af3a4d..24619fca 100644 --- a/lib/net.c +++ b/lib/net.c @@ -64,8 +64,8 @@ const u16 net_max_text_length[] = { [NET_FLOW6] = 0, /* "flow6 { ... }" */ [NET_MREQ4] = 15, /* "255.255.255.255" */ [NET_MREQ6] = 39, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" */ - [NET_MGRP4] = 15, /* "255.255.255.255" */ - [NET_MGRP6] = 39, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" */ + [NET_MGRP4] = 20, /* "(*, 255.255.255.255)" */ + [NET_MGRP6] = 44, /* "(*, ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff)" */ [NET_MPLS] = 7, /* "1048575" */ }; @@ -123,9 +123,9 @@ net_format(const net_addr *N, char *buf, int buflen) case NET_MREQ6: return bsnprintf(buf, buflen, "%I6", n->mreq6.grp); case NET_MGRP4: - return bsnprintf(buf, buflen, "%I4", n->mgrp4.grp); + return bsnprintf(buf, buflen, "(*, %I4)", n->mgrp4.grp); case NET_MGRP6: - return bsnprintf(buf, buflen, "%I6", n->mgrp6.grp); + return bsnprintf(buf, buflen, "(*, %I6)", n->mgrp6.grp); case NET_MPLS: return bsnprintf(buf, buflen, "%u", n->mpls.label); } diff --git a/lib/net.h b/lib/net.h index de25ad6d..39f0f905 100644 --- a/lib/net.h +++ b/lib/net.h @@ -49,10 +49,14 @@ #define NB_MREQ (NB_MREQ4 | NB_MREQ6) #define NB_MGRP (NB_MGRP4 | NB_MGRP6) +/* FIXME: Better validation of (NET, RTD) combinations */ #define NB_HOST (NB_IP | NB_VPN | NB_ROA) -#define NB_DEST (NB_IP | NB_VPN | NB_MPLS) +// #define NB_DEST (NB_IP | NB_VPN | NB_MPLS) +#define NB_DEST (NB_IP | NB_VPN | NB_MGRP | NB_MPLS) #define NB_ANY 0xffffffff +#define MIFS_MAX 32 + typedef struct net_addr { u8 type; diff --git a/nest/iface.c b/nest/iface.c index 54c16c58..b165e6e7 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -37,8 +37,10 @@ static pool *if_pool; list iface_list; +struct mif_group *global_mif_group; static void if_recalc_preferred(struct iface *i); +static struct mif_group *mif_get_group(void); /** * ifa_dump - dump interface address @@ -714,6 +716,61 @@ if_init(void) if_pool = rp_new(&root_pool, "Interfaces"); init_list(&iface_list); neigh_init(if_pool); + global_mif_group = mif_get_group(); +} + +/* + * Multicast Ifaces + */ + +static struct mif_group * +mif_get_group(void) +{ + struct mif_group *grp = mb_allocz(if_pool, sizeof(struct mif_group)); + init_list(&grp->sockets); + + return grp; +} + +static inline int u32_cto(uint x) { return ffs(~x) - 1; } + +struct mif * +mif_get(struct mif_group *grp, struct iface *iface) +{ + WALK_ARRAY(grp->mifs, MIFS_MAX, mif) + if (mif && (mif->iface == iface)) + return mif->uc++, mif; + + int i = u32_cto(grp->indexes); + if (i < 0) + return NULL; + + struct mif *mif = mb_allocz(if_pool, sizeof(struct mif)); + mif->iface = iface; + mif->index = i; + mif->uc = 1; + init_list(&mif->sockets); + + grp->mifs[mif->index] = mif; + MIFS_SET(mif, grp->indexes); + + return mif; +} + +void +mif_free(struct mif_group *grp, struct mif *mif) +{ + if (--mif->uc) + return; + + node *n; + WALK_LIST_FIRST(n, mif->sockets) + rem_node(n); + + grp->mifs[mif->index] = NULL; + MIFS_CLR(mif, grp->indexes); + + mb_free(mif); } /* diff --git a/nest/iface.h b/nest/iface.h index ab3f8f35..dc44dfad 100644 --- a/nest/iface.h +++ b/nest/iface.h @@ -13,6 +13,7 @@ #include "lib/ip.h" extern list iface_list; +extern struct mif_group *global_mif_group; struct proto; struct pool; @@ -44,6 +45,20 @@ struct iface { list neighbors; /* All neighbors on this interface */ }; +struct mif { + struct iface *iface; + uint index; /* MIF (VIF) index for multicast routes */ + uint uc; /* Use count */ + list sockets; /* Listening per-iface IGMP sockets */ +}; + +struct mif_group { + uint indexes; + uint uc; /* Use count, not implemented */ + list sockets; /* Listening global IGMP sockets */ + struct mif *mifs[MIFS_MAX]; +}; + #define IF_UP 1 /* Currently just IF_ADMIN_UP */ #define IF_MULTIACCESS 2 #define IF_BROADCAST 4 @@ -115,6 +130,13 @@ struct iface *if_find_by_name(char *); struct iface *if_get_by_name(char *); void if_recalc_all_preferred_addresses(void); +struct mif *mif_get(struct mif_group *grp, struct iface *iface); +void mif_free(struct mif_group *grp, struct mif *mif); + +#define MIFS_SET(mif,m) ((m) |= (1 << (mif)->index)) +#define MIFS_CLR(mif,m) ((m) &= ~(1 << (mif)->index)) +#define MIFS_ISSET(mif,m) ((m) & (1 << (mif)->index)) + /* The Neighbor Cache */ diff --git a/nest/route.h b/nest/route.h index d47645f8..b2ae4686 100644 --- a/nest/route.h +++ b/nest/route.h @@ -152,6 +152,7 @@ typedef struct rtable { int pipe_busy; /* Pipe loop detection */ int use_count; /* Number of protocols using this table */ struct hostcache *hostcache; + // struct mif_group *mif_group; struct rtable_config *config; /* Configuration of this table */ struct config *deleted; /* Table doesn't exist in current configuration, * delete as soon as use_count becomes 0 and remove @@ -249,9 +250,6 @@ typedef struct rte { u8 best; /* Best route in network, propagated to core */ u32 metric; /* Kernel metric */ } krt; - struct { - u32 iifs, oifs; /* Bitmaps for iifs and oifs. Use RTE_MGRP_* macros to manipulate. */ - } mkrt; } u; } rte; @@ -637,6 +635,19 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls); } +/* For RTD_MULTICAST, we encode iifs and oifs to nh.gw */ +static inline u32 rta_iifs(rta *a) +{ return _I0(a->nh.gw); } + +static inline void rta_set_iifs(rta *a, u32 val) +{ _I0(a->nh.gw) = val; } + +static inline u32 rta_oifs(rta *a) +{ return _I1(a->nh.gw); } + +static inline void rta_set_oifs(rta *a, u32 val) +{ _I1(a->nh.gw) = val; } + /* * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills * rta->hostentry field. New hostentry has zero use count. Cached rta locks its diff --git a/nest/rt-show.c b/nest/rt-show.c index 41a141a2..b8366881 100644 --- a/nest/rt-show.c +++ b/nest/rt-show.c @@ -28,6 +28,30 @@ rt_show_table(struct cli *c, struct rt_show_data *d) d->last_table = d->tab; } +static void +rt_show_mifs(char *key, struct mif_group *grp, u32 mifs) +{ + uint blen = 512; + char *buf = alloca(blen + 8); + char *pos = buf; + pos[0] = 0; + + WALK_ARRAY(grp->mifs, MIFS_MAX, mif) + if (mif && MIFS_ISSET(mif, mifs)) + { + int i = bsnprintf(pos, blen, " %s", mif->iface->name); + if (i < 0) + { + bsprintf(pos, " ..."); + break; + } + + ADVANCE(pos, blen, i); + } + + cli_msg(-1007, "%s%s", key, buf); +} + static void rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tmpa) { @@ -92,6 +116,12 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm nh->iface->name, mpls, onlink, weight); } + if (a->dest == RTD_MULTICAST) + { + rt_show_mifs("\tfrom", global_mif_group, rta_iifs(a)); + rt_show_mifs("\tto", global_mif_group, rta_oifs(a)); + } + if (d->verbose) rta_show(c, a, tmpa); }