From c10421d3d4b5f23dc953c887332bdb6e80ae0540 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sat, 3 Apr 1999 13:05:18 +0000 Subject: More changes to the kernel syncer. o Now compatible with filtering. o Learning of kernel routes supported only on CONFIG_SELF_CONSCIOUS systems (on the others it's impossible to get it semantically correct). o Learning now stores all of its routes in a separate fib and selects the ones the kernel really uses for forwarding packets. o Better treatment of CONFIG_AUTO_ROUTES ports. o Lots of internal changes. --- sysdep/linux/krt-scan.c | 2 +- sysdep/linux/netlink/netlink.c | 39 +++-- sysdep/unix/krt-set.c | 38 +--- sysdep/unix/krt.Y | 8 +- sysdep/unix/krt.c | 389 ++++++++++++++++++++++++++++++++++------- sysdep/unix/krt.h | 16 +- 6 files changed, 385 insertions(+), 107 deletions(-) (limited to 'sysdep') diff --git a/sysdep/linux/krt-scan.c b/sysdep/linux/krt-scan.c index 9d19f71..85e092d 100644 --- a/sysdep/linux/krt-scan.c +++ b/sysdep/linux/krt-scan.c @@ -127,7 +127,7 @@ krt_parse_entry(byte *ent, struct krt_proto *p) e = rte_get_temp(&a); e->net = net; - e->u.krt_sync.src = KRT_SRC_UNKNOWN; + e->u.krt.src = KRT_SRC_UNKNOWN; krt_got_route(p, e); } diff --git a/sysdep/linux/netlink/netlink.c b/sysdep/linux/netlink/netlink.c index 00736ec..326c174 100644 --- a/sysdep/linux/netlink/netlink.c +++ b/sysdep/linux/netlink/netlink.c @@ -276,7 +276,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) ifi = if_find_by_index(i->ifi_index); if (!new) { - DBG("KRT: IF%d(%s) goes down\n", i->ifi_index, name); + DBG("KIF: IF%d(%s) goes down\n", i->ifi_index, name); if (ifi && !scan) { memcpy(&f, ifi, sizeof(struct iface)); @@ -286,7 +286,7 @@ nl_parse_link(struct nlmsghdr *h, int scan) } else { - DBG("KRT: IF%d(%s) goes up (mtu=%d,flg=%x)\n", i->ifi_index, name, mtu, i->ifi_flags); + DBG("KIF: IF%d(%s) goes up (mtu=%d,flg=%x)\n", i->ifi_index, name, mtu, i->ifi_flags); if (ifi) memcpy(&f, ifi, sizeof(f)); else @@ -332,14 +332,14 @@ nl_parse_addr(struct nlmsghdr *h) } if (i->ifa_flags & IFA_F_SECONDARY) { - DBG("KRT: Received address message for secondary address which is not supported.\n"); /* FIXME */ + DBG("KIF: Received address message for secondary address which is not supported.\n"); /* FIXME */ return; } ifi = if_find_by_index(i->ifa_index); if (!ifi) { - log(L_ERR "KRT: Received address message for unknown interface %d\n", i->ifa_index); + log(L_ERR "KIF: Received address message for unknown interface %d\n", i->ifa_index); return; } memcpy(&f, ifi, sizeof(f)); @@ -347,14 +347,14 @@ nl_parse_addr(struct nlmsghdr *h) if (i->ifa_prefixlen > 32 || i->ifa_prefixlen == 31 || (f.flags & IF_UNNUMBERED) && i->ifa_prefixlen != 32) { - log(L_ERR "KRT: Invalid prefix length for interface %s: %d\n", f.name, i->ifa_prefixlen); + log(L_ERR "KIF: Invalid prefix length for interface %s: %d\n", f.name, i->ifa_prefixlen); new = 0; } f.ip = f.brd = f.opposite = IPA_NONE; if (!new) { - DBG("KRT: IF%d IP address deleted\n"); + DBG("KIF: IF%d IP address deleted\n"); f.pxlen = 0; } else @@ -374,7 +374,7 @@ nl_parse_addr(struct nlmsghdr *h) } /* else a NBMA link */ f.prefix = ipa_and(f.ip, ipa_mkmask(f.pxlen)); - DBG("KRT: IF%d IP address set to %I, net %I/%d, brd %I, opp %I\n", f.index, f.ip, f.prefix, f.pxlen, f.brd, f.opposite); + DBG("KIF: IF%d IP address set to %I, net %I/%d, brd %I, opp %I\n", f.index, f.ip, f.prefix, f.pxlen, f.brd, f.opposite); } if_update(&f); } @@ -485,12 +485,8 @@ nl_send_route(rte *e, int new) } void -krt_set_notify(struct proto *p, net *n, rte *new, rte *old) +krt_set_notify(struct krt_proto *p, net *n, rte *new, rte *old) { - if (old && !krt_capable(old)) - old = NULL; - if (new && !krt_capable(new)) - new = NULL; if (old && new && old->attrs->tos == new->attrs->tos) { /* FIXME: Priorities should be identical as well, but we don't use them yet. */ @@ -546,6 +542,7 @@ nl_parse_route(struct krt_proto *p, struct nlmsghdr *h, int scan) return; if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) || (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || + (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) || (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))) { log(L_ERR "nl_parse_route: Malformed message received"); @@ -649,9 +646,22 @@ nl_parse_route(struct krt_proto *p, struct nlmsghdr *h, int scan) DBG("KRT: Ignoring route with type=%d\n", i->rtm_type); return; } + + if (i->rtm_scope != RT_SCOPE_UNIVERSE) /* FIXME: Other scopes? */ + { + DBG("KRT: Ignoring route with scope=%d\n", i->rtm_scope); + return; + } + e = rte_get_temp(&ra); e->net = net; - e->u.krt_sync.src = src; + e->u.krt.src = src; + e->u.krt.proto = i->rtm_protocol; + e->u.krt.type = i->rtm_type; + if (a[RTA_PRIORITY]) + memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); + else + e->u.krt.metric = 0; if (scan) krt_got_route(p, e); else @@ -797,7 +807,7 @@ krt_scan_start(struct krt_proto *p) { init_list(&p->scan.temp_ifs); nl_open(); - if (KRT_CF->scan.async) + if (KRT_CF->scan.async) /* FIXME: Async is for debugging only. Get rid of it some day. */ nl_open_async(p); } @@ -810,4 +820,5 @@ void krt_if_start(struct kif_proto *p) { nl_open(); + /* FIXME: nl_open_async() after scan.async is gone */ } diff --git a/sysdep/unix/krt-set.c b/sysdep/unix/krt-set.c index 5be01c4..435587b 100644 --- a/sysdep/unix/krt-set.c +++ b/sysdep/unix/krt-set.c @@ -77,41 +77,19 @@ krt_ioctl(int ioc, rte *e, char *name) log(L_ERR "%s(%I/%d): %m", name, net->n.prefix, net->n.pxlen); } -static inline void -krt_remove_route(rte *old) +void +krt_set_notify(struct krt_proto *p, net *net, rte *new, rte *old) { - net *net = old->net; - - if (!krt_capable(old)) + if (old) { - DBG("krt_remove_route(ignored %I/%d)\n", net->n.prefix, net->n.pxlen); - return; + DBG("krt_remove_route(%I/%d)\n", net->n.prefix, net->n.pxlen); + krt_ioctl(SIOCDELRT, old, "SIOCDELRT"); } - DBG("krt_remove_route(%I/%d)\n", net->n.prefix, net->n.pxlen); - krt_ioctl(SIOCDELRT, old, "SIOCDELRT"); -} - -static inline void -krt_add_route(rte *new) -{ - net *net = new->net; - - if (!krt_capable(new)) + if (new) { - DBG("krt_add_route(ignored %I/%d)\n", net->n.prefix, net->n.pxlen); - return; + DBG("krt_add_route(%I/%d)\n", net->n.prefix, net->n.pxlen); + krt_ioctl(SIOCADDRT, new, "SIOCADDRT"); } - DBG("krt_add_route(%I/%d)\n", net->n.prefix, net->n.pxlen); - krt_ioctl(SIOCADDRT, new, "SIOCADDRT"); -} - -void -krt_set_notify(struct proto *x, net *net, rte *new, rte *old) -{ - if (old) - krt_remove_route(old); - if (new) - krt_add_route(new); } void diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y index 083df7d..de25cad 100644 --- a/sysdep/unix/krt.Y +++ b/sysdep/unix/krt.Y @@ -45,7 +45,13 @@ kern_item: /* Scan time of 0 means scan on startup only */ THIS_KRT->scan_time = $3; } - | LEARN bool { THIS_KRT->learn = $2; } + | LEARN bool { + THIS_KRT->learn = $2; +#ifndef KRT_ALLOW_LEARN + if ($2) + cf_error("Learning of kernel routes not supported in this configuration"); +#endif + } ; /* Kernel interface protocol */ diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 096b9da..5d694f6 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -17,6 +17,8 @@ #include "unix.h" #include "krt.h" +static int krt_uptodate(rte *k, rte *e); + /* * Global resources */ @@ -106,6 +108,247 @@ struct protocol proto_unix_iface = { shutdown: kif_shutdown, }; +/* + * Inherited Routes + */ + +#ifdef KRT_ALLOW_LEARN + +static inline int +krt_same_key(rte *a, rte *b) +{ + return a->u.krt.proto == b->u.krt.proto && + a->u.krt.metric == b->u.krt.metric && + a->u.krt.type == b->u.krt.type; +} + +static void +krt_learn_announce_update(struct krt_proto *p, rte *e) +{ + net *n = e->net; + rta *aa = rta_clone(e->attrs); + rte *ee = rte_get_temp(aa); + net *nn = net_get(p->p.table, 0, n->n.prefix, n->n.pxlen); /* FIXME: TOS */ + ee->net = nn; + ee->pflags = 0; + ee->u.krt = e->u.krt; + rte_update(nn, &p->p, ee); +} + +static void +krt_learn_announce_delete(struct krt_proto *p, net *n) +{ + n = net_find(p->p.table, 0, n->n.prefix, n->n.pxlen); /* FIXME: TOS */ + if (n) + rte_update(n, &p->p, NULL); +} + +static void +krt_learn_scan(struct krt_proto *p, rte *e) +{ + net *n0 = e->net; + net *n = net_get(&p->krt_table, 0, n0->n.prefix, n0->n.pxlen); /* FIXME: TOS */ + rte *m, **mm; + + e->attrs->source = RTS_INHERIT; + + for(mm=&n->routes; m = *mm; mm=&m->next) + if (krt_same_key(m, e)) + break; + if (m) + { + if (krt_uptodate(m, e)) + { + DBG("krt_learn_scan: SEEN\n"); + rte_free(e); + m->u.krt.seen = 1; + } + else + { + DBG("krt_learn_scan: OVERRIDE\n"); + *mm = m->next; + rte_free(m); + m = NULL; + } + } + else + DBG("krt_learn_scan: CREATE\n"); + if (!m) + { + e->attrs = rta_lookup(e->attrs); + e->next = n->routes; + n->routes = e; + e->u.krt.seen = 1; + } +} + +/* FIXME: Add dump function */ + +static void +krt_learn_prune(struct krt_proto *p) +{ + struct fib *fib = &p->krt_table.fib; + struct fib_iterator fit; + + DBG("Pruning inheritance data...\n"); + + FIB_ITERATE_INIT(&fit, fib); +again: + FIB_ITERATE_START(fib, &fit, f) + { + net *n = (net *) f; + rte *e, **ee, *best, **pbest, *old_best; + + old_best = n->routes; + best = NULL; + pbest = NULL; + ee = &n->routes; + while (e = *ee) + { + if (!e->u.krt.seen) + { + *ee = e->next; + rte_free(e); + continue; + } + if (!best || best->u.krt.metric > e->u.krt.metric) + { + best = e; + pbest = ee; + } + e->u.krt.seen = 0; + ee = &e->next; + } + if (!n->routes) + { + DBG("%I/%d: deleting\n", n->n.prefix, n->n.pxlen); + if (old_best) + { + krt_learn_announce_delete(p, n); + n->n.flags &= ~KRF_INSTALLED; + } + FIB_ITERATE_PUT(&fit, f); + fib_delete(fib, f); + goto again; + } + *pbest = best->next; + best->next = n->routes; + n->routes = best; + if (best != old_best || !(n->n.flags & KRF_INSTALLED)) + { + DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); + krt_learn_announce_update(p, best); + n->n.flags |= KRF_INSTALLED; + } + else + DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric); + } + FIB_ITERATE_END(f); +} + +static void +krt_learn_async(struct krt_proto *p, rte *e, int new) +{ + net *n0 = e->net; + net *n = net_get(&p->krt_table, 0, n0->n.prefix, n0->n.pxlen); /* FIXME: TOS */ + rte *g, **gg, *best, **bestp, *old_best; + + e->attrs->source = RTS_INHERIT; + + old_best = n->routes; + for(gg=&n->routes; g = *gg; gg = &g->next) + if (krt_same_key(g, e)) + break; + if (new) + { + if (g) + { + if (krt_uptodate(g, e)) + { + DBG("krt_learn_async: same\n"); + rte_free(e); + return; + } + DBG("krt_learn_async: update\n"); + *gg = g->next; + rte_free(g); + } + else + DBG("krt_learn_async: create\n"); + e->attrs = rta_lookup(e->attrs); + e->next = n->routes; + n->routes = e; + } + else if (!g) + { + DBG("krt_learn_async: not found\n"); + rte_free(e); + return; + } + else + { + DBG("krt_learn_async: delete\n"); + *gg = g->next; + rte_free(e); + rte_free(g); + } + best = n->routes; + bestp = &n->routes; + for(gg=&n->routes; g=*gg; gg=&g->next) + if (best->u.krt.metric > g->u.krt.metric) + { + best = g; + bestp = gg; + } + if (best) + { + *bestp = best->next; + best->next = n->routes; + n->routes = best; + } + if (best != old_best) + { + DBG("krt_learn_async: distributing change\n"); + if (best) + { + krt_learn_announce_update(p, best); + n->n.flags |= KRF_INSTALLED; + } + else + { + n->routes = NULL; + krt_learn_announce_delete(p, n); + n->n.flags &= ~KRF_INSTALLED; + } + } +} + +static void +krt_learn_init(struct krt_proto *p) +{ + if (KRT_CF->learn) + rt_setup(p->p.pool, &p->krt_table, "Inherited"); +} + +static void +krt_dump(struct proto *P) +{ + struct krt_proto *p = (struct krt_proto *) P; + + if (!KRT_CF->learn) + return; + debug("KRT: Table of inheritable routes\n"); + rt_dump(&p->krt_table); +} + +static void +krt_dump_attrs(rte *e) +{ + debug(" [m=%d,p=%d,t=%d]", e->u.krt.metric, e->u.krt.proto, e->u.krt.type); +} + +#endif + /* * Routes */ @@ -128,13 +371,12 @@ krt_flush_routes(struct krt_proto *p) { rta *a = e->attrs; if (a->source != RTS_DEVICE && a->source != RTS_INHERIT) - krt_set_notify(&p->p, e->net, NULL, e); + krt_set_notify(p, e->net, NULL, e); } } FIB_WALK_END; } -/* FIXME: Inbound/outbound route filtering? */ /* FIXME: Synchronization of multiple routing tables? */ static int @@ -165,41 +407,53 @@ krt_got_route(struct krt_proto *p, rte *e) { rte *old; net *net = e->net; - int src = e->u.krt_sync.src; + int src = e->u.krt.src; int verdict; - if (net->n.flags) +#ifdef CONFIG_AUTO_ROUTES + if (e->attrs->dest == RTD_DEVICE) + { + /* It's a device route. Probably a kernel-generated one. */ + verdict = KRF_IGNORE; + goto sentenced; + } +#endif + +#ifdef KRT_ALLOW_LEARN + if (src == KRT_SRC_ALIEN) + { + if (KRT_CF->learn) + krt_learn_scan(p, e); + else + DBG("krt_parse_entry: Alien route, ignoring\n"); + return; + } +#endif + + if (net->n.flags & KRF_VERDICT_MASK) { /* Route to this destination was already seen. Strange, but it happens... */ DBG("Already seen.\n"); return; } - if (old = net->routes) + if (net->n.flags & KRF_INSTALLED) { - if (!krt_capable(old)) - { -#ifdef CONFIG_AUTO_ROUTES - if (old->attrs->source == RTS_DEVICE) - verdict = KRF_SEEN; - else -#endif - verdict = krt_capable(e) ? KRF_DELETE : KRF_SEEN; - } - else if (krt_uptodate(e, net->routes)) + old = net->routes; + ASSERT(old); + if (krt_uptodate(e, old)) verdict = KRF_SEEN; else verdict = KRF_UPDATE; } - else if (KRT_CF->learn && !net->routes && (src == KRT_SRC_ALIEN || src < 0)) - verdict = KRF_LEARN; else verdict = KRF_DELETE; - DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "LEARN" }) [verdict]); +sentenced: + DBG("krt_parse_entry: verdict=%s\n", ((char *[]) { "CREATE", "SEEN", "UPDATE", "DELETE", "IGNORE" }) [verdict]); - net->n.flags = verdict; - if (verdict != KRF_SEEN) + net->n.flags = (net->n.flags & ~KRF_VERDICT_MASK) | verdict; + if (verdict == KRF_UPDATE || verdict == KRF_DELETE) { /* Get a cached copy of attributes and link the route */ rta *a = e->attrs; @@ -227,10 +481,10 @@ krt_prune(struct krt_proto *p) FIB_WALK(&t->fib, f) { net *n = (net *) f; - int verdict = f->flags; + int verdict = f->flags & KRF_VERDICT_MASK; rte *new, *old; - if (verdict != KRF_CREATE && verdict != KRF_SEEN) + if (verdict != KRF_CREATE && verdict != KRF_SEEN && verdict != KRF_IGNORE) { old = n->routes; n->routes = old->next; @@ -242,44 +496,37 @@ krt_prune(struct krt_proto *p) switch (verdict) { case KRF_CREATE: - if (new) + if (new && (f->flags & KRF_INSTALLED)) { - if (new->attrs->source == RTS_INHERIT) - { - DBG("krt_prune: removing inherited %I/%d\n", n->n.prefix, n->n.pxlen); - rte_update(n, pp, NULL); - } - else if (krt_capable(new)) - { - DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); - krt_set_notify(pp, n, new, NULL); - } + DBG("krt_prune: reinstalling %I/%d\n", n->n.prefix, n->n.pxlen); + krt_set_notify(p, n, new, NULL); } break; case KRF_SEEN: + case KRF_IGNORE: /* Nothing happens */ break; case KRF_UPDATE: DBG("krt_prune: updating %I/%d\n", n->n.prefix, n->n.pxlen); - krt_set_notify(pp, n, new, old); + krt_set_notify(p, n, new, old); break; case KRF_DELETE: DBG("krt_prune: deleting %I/%d\n", n->n.prefix, n->n.pxlen); - krt_set_notify(pp, n, NULL, old); - break; - case KRF_LEARN: - DBG("krt_prune: learning %I/%d\n", n->n.prefix, n->n.pxlen); - rte_update(n, pp, new); + krt_set_notify(p, n, NULL, old); break; default: bug("krt_prune: invalid route status"); } - if (old) rte_free(old); - f->flags = 0; + f->flags &= ~KRF_VERDICT_MASK; } FIB_WALK_END; + +#ifdef KRT_ALLOW_LEARN + if (KRT_CF->learn) + krt_learn_prune(p); +#endif } void @@ -287,7 +534,7 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new) { net *net = e->net; rte *old = net->routes; - int src = e->u.krt_sync.src; + int src = e->u.krt.src; switch (src) { @@ -295,26 +542,22 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new) ASSERT(0); case KRT_SRC_REDIRECT: DBG("It's a redirect, kill him! Kill! Kill!\n"); - krt_set_notify(&p->p, net, NULL, e); + krt_set_notify(p, net, NULL, e); break; - default: /* Alien or unspecified */ - if (KRT_CF->learn && new) + case KRT_SRC_ALIEN: +#ifdef KRT_ALLOW_LEARN + if (KRT_CF->learn) { - /* - * FIXME: This is limited to one inherited route per destination as we - * use single protocol for all inherited routes. Probably leave it - * as-is (and document it :)), because the correct solution is to - * use multiple kernel tables anyway. - */ - DBG("Learning\n"); - rte_update(net, &p->p, e); - } - else - { - DBG("Discarding\n"); - rte_update(net, &p->p, NULL); + krt_learn_async(p, e, new); + return; } +#endif + /* Fall-thru */ + default: + DBG("Discarding\n"); + rte_update(net, &p->p, NULL); } + rte_free(e); } /* @@ -334,6 +577,26 @@ krt_scan(timer *t) krt_prune(p); } +/* + * Updates + */ + +static void +krt_notify(struct proto *P, net *net, rte *new, rte *old) +{ + struct krt_proto *p = (struct krt_proto *) P; + + if (new && (!krt_capable(new) || new->attrs->source == RTS_INHERIT)) + new = NULL; + if (!(net->n.flags & KRF_INSTALLED)) + old = NULL; + if (new) + net->n.flags |= KRF_INSTALLED; + else + net->n.flags &= ~KRF_INSTALLED; + krt_set_notify(p, net, new, old); +} + /* * Protocol glue */ @@ -345,6 +608,10 @@ krt_start(struct proto *P) { struct krt_proto *p = (struct krt_proto *) P; +#ifdef KRT_ALLOW_LEARN + krt_learn_init(p); +#endif + krt_scan_start(p); krt_set_start(p); @@ -380,7 +647,7 @@ krt_init(struct proto_config *c) { struct krt_proto *p = proto_new(c, sizeof(struct krt_proto)); - p->p.rt_notify = krt_set_notify; + p->p.rt_notify = krt_notify; return &p->p; } @@ -390,4 +657,8 @@ struct protocol proto_unix_kernel = { init: krt_init, start: krt_start, shutdown: krt_shutdown, +#ifdef KRT_ALLOW_LEARN + dump: krt_dump, + dump_attrs: krt_dump_attrs, +#endif }; diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index bde509c..df9e6d3 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -20,11 +20,20 @@ struct kif_proto; /* Flags stored in net->n.flags */ +#define KRF_VERDICT_MASK 0x0f #define KRF_CREATE 0 /* Not seen in kernel table */ #define KRF_SEEN 1 /* Seen in kernel table during last scan */ #define KRF_UPDATE 2 /* Need to update this entry */ #define KRF_DELETE 3 /* Should be deleted */ -#define KRF_LEARN 4 /* We should learn this route */ +#define KRF_IGNORE 4 /* To be ignored */ + +#define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */ + +/* Whenever we recognize our own routes, we allow learing of foreign routes */ + +#ifdef CONFIG_SELF_CONSCIOUS +#define KRT_ALLOW_LEARN +#endif /* krt.c */ @@ -45,6 +54,9 @@ struct krt_proto { struct krt_set_status set; struct krt_scan_status scan; struct krt_if_status iface; +#ifdef KRT_ALLOW_LEARN + struct rtable krt_table; /* Internal table of inherited routes */ +#endif }; extern struct proto_config *cf_krt; @@ -92,7 +104,7 @@ void krt_set_start(struct krt_proto *); void krt_set_shutdown(struct krt_proto *); int krt_capable(rte *e); -void krt_set_notify(struct proto *x, net *net, rte *new, rte *old); +void krt_set_notify(struct krt_proto *x, net *net, rte *new, rte *old); /* krt-iface.c */ -- cgit v1.2.3