%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
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));
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_
;
#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); }
[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" */
};
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);
}
#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;
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
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);
}
/*
#include "lib/ip.h"
extern list iface_list;
+extern struct mif_group *global_mif_group;
struct proto;
struct pool;
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
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 */
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
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;
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
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)
{
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);
}