+static void netlink_inst_set_mtu(void *sst, int32_t new_mtu)
+{
+ struct netlink_client *c=sst;
+
+ c->mtu=new_mtu;
+}
+
+static void netlink_inst_reg(void *sst, netlink_deliver_fn *deliver,
+ void *dst)
+{
+ struct netlink_client *c=sst;
+
+ c->deliver=deliver;
+ c->dst=dst;
+}
+
+static struct flagstr netlink_option_table[]={
+ { "soft", OPT_SOFTROUTE },
+ { "allow-route", OPT_ALLOWROUTE },
+ { NULL, 0}
+};
+/* This is the routine that gets called when the closure that's
+ returned by an invocation of a netlink device closure (eg. tun,
+ userv-ipif) is invoked. It's used to create routes and pass in
+ information about them; the closure it returns is used by site
+ code. */
+static closure_t *netlink_inst_create(struct netlink *st,
+ struct cloc loc, dict_t *dict)
+{
+ struct netlink_client *c;
+ string_t name;
+ struct ipset *networks;
+ uint32_t options,priority;
+ int32_t mtu;
+ list_t *l;
+
+ name=dict_read_string(dict, "name", True, st->name, loc);
+
+ l=dict_lookup(dict,"routes");
+ if (!l)
+ cfgfatal(loc,st->name,"required parameter \"routes\" not found\n");
+ networks=string_list_to_ipset(l,loc,st->name,"routes");
+ options=string_list_to_word(dict_lookup(dict,"options"),
+ netlink_option_table,st->name);
+
+ priority=dict_read_number(dict,"priority",False,st->name,loc,0);
+ mtu=dict_read_number(dict,"mtu",False,st->name,loc,0);
+
+ if ((options&OPT_SOFTROUTE) && !st->set_routes) {
+ cfgfatal(loc,st->name,"this netlink device does not support "
+ "soft routes.\n");
+ return NULL;
+ }
+
+ if (options&OPT_SOFTROUTE) {
+ /* XXX for now we assume that soft routes require root privilege;
+ this may not always be true. The device driver can tell us. */
+ require_root_privileges=True;
+ require_root_privileges_explanation="netlink: soft routes";
+ if (st->ptp) {
+ cfgfatal(loc,st->name,"point-to-point netlinks do not support "
+ "soft routes.\n");
+ return NULL;
+ }
+ }
+
+ /* Check that nets are a subset of st->remote_networks;
+ refuse to register if they are not. */
+ if (!ipset_is_subset(st->remote_networks,networks)) {
+ cfgfatal(loc,st->name,"routes are not allowed\n");
+ return NULL;
+ }
+
+ c=safe_malloc(sizeof(*c),"netlink_inst_create");
+ c->cl.description=name;
+ c->cl.type=CL_NETLINK;
+ c->cl.apply=NULL;
+ c->cl.interface=&c->ops;
+ c->ops.st=c;
+ c->ops.reg=netlink_inst_reg;
+ c->ops.deliver=netlink_inst_incoming;
+ c->ops.set_quality=netlink_set_quality;
+ c->ops.set_mtu=netlink_inst_set_mtu;
+ c->nst=st;
+
+ c->networks=networks;
+ c->subnets=ipset_to_subnet_list(networks);
+ c->priority=priority;
+ c->deliver=NULL;
+ c->dst=NULL;
+ c->name=name;
+ c->link_quality=LINK_QUALITY_UNUSED;
+ c->mtu=mtu?mtu:st->mtu;
+ c->options=options;
+ c->outcount=0;
+ c->up=False;
+ c->kup=False;
+ c->next=st->clients;
+ st->clients=c;
+ assert(st->n_clients < INT_MAX);
+ st->n_clients++;
+
+ return &c->cl;
+}
+
+static list_t *netlink_inst_apply(closure_t *self, struct cloc loc,
+ dict_t *context, list_t *args)
+{
+ struct netlink *st=self->interface;
+
+ dict_t *dict;
+ item_t *item;
+ closure_t *cl;
+
+ item=list_elem(args,0);
+ if (!item || item->type!=t_dict) {
+ cfgfatal(loc,st->name,"must have a dictionary argument\n");
+ }
+ dict=item->data.dict;
+
+ cl=netlink_inst_create(st,loc,dict);
+
+ return new_closure(cl);
+}
+