aboutsummaryrefslogtreecommitdiffstats
path: root/net/bird
diff options
context:
space:
mode:
authorsem <sem@FreeBSD.org>2012-01-26 23:17:58 +0800
committersem <sem@FreeBSD.org>2012-01-26 23:17:58 +0800
commitb11925f0f929eba1514c8d4a97d43d18860baa47 (patch)
treeb58c337fc3548a2ca95c0b420cb12818a6612a3a /net/bird
parent48f1edc7fc96e2af8a0f0077515d2da9317ed37f (diff)
downloadfreebsd-ports-gnome-b11925f0f929eba1514c8d4a97d43d18860baa47.tar.gz
freebsd-ports-gnome-b11925f0f929eba1514c8d4a97d43d18860baa47.tar.zst
freebsd-ports-gnome-b11925f0f929eba1514c8d4a97d43d18860baa47.zip
- Update to 1.3.6
- Add all current git patches - for net/bird add beta firewall protocol support Submitted by: mainteiner
Diffstat (limited to 'net/bird')
-rw-r--r--net/bird/Makefile10
-rw-r--r--net/bird/distinfo4
-rw-r--r--net/bird/files/firewall_support.patch982
-rw-r--r--net/bird/files/patch-136-post.diff507
-rw-r--r--net/bird/files/patch-protocoltemplates.diff1085
5 files changed, 1498 insertions, 1090 deletions
diff --git a/net/bird/Makefile b/net/bird/Makefile
index 2aaffd3a170a..ded9c723c7c8 100644
--- a/net/bird/Makefile
+++ b/net/bird/Makefile
@@ -6,8 +6,7 @@
#
PORTNAME= bird
-PORTVERSION= 1.3.4
-PORTREVESION= 1
+PORTVERSION= 1.3.6
CATEGORIES= net
MASTER_SITES= ftp://bird.network.cz/pub/bird/
@@ -18,7 +17,8 @@ USE_BISON= build
USE_GMAKE= yes
GNU_CONFIGURE= yes
-OPTIONS= FIBS "Enable multiple fib support" Off
+OPTIONS= FIBS "Enable multiple fib support" Off \
+ FIREWALL "Enable firewall protocol" Off
MAKE_JOBS_UNSAFE= yes
@@ -30,6 +30,10 @@ USE_RC_SUBR= bird
EXTRA_PATCHES+= ${FILESDIR}/fibs.diff
.endif
+.if defined(WITH_FIREWALL)
+EXTRA_PATCHES+= ${FILESDIR}/firewall_support.patch
+.endif
+
post-install:
@if [ ! -f ${PREFIX}/etc/bird.conf ]; then \
${CP} -p ${PREFIX}/etc/bird.conf.example ${PREFIX}/etc/bird.conf ; \
diff --git a/net/bird/distinfo b/net/bird/distinfo
index 276df66fd0ac..9f46e07d9c67 100644
--- a/net/bird/distinfo
+++ b/net/bird/distinfo
@@ -1,2 +1,2 @@
-SHA256 (bird-1.3.4.tar.gz) = 0b97c7486515b3ec81b34935859c0b66d92e2cc3f1f001e640d5d38e10c61dc6
-SIZE (bird-1.3.4.tar.gz) = 759228
+SHA256 (bird-1.3.6.tar.gz) = 5d060256c179bceff861b7533831adc690eb64d27d14fb1ddfba407eb83d870a
+SIZE (bird-1.3.6.tar.gz) = 864775
diff --git a/net/bird/files/firewall_support.patch b/net/bird/files/firewall_support.patch
new file mode 100644
index 000000000000..de1275162523
--- /dev/null
+++ b/net/bird/files/firewall_support.patch
@@ -0,0 +1,982 @@
+From c99266ef16e66f94f22a2f78dcea82c795c4611f Mon Sep 17 00:00:00 2001
+From: Alexander V. Chernikov <melifaro@ipfw.ru>
+Date: Fri, 23 Dec 2011 13:47:59 +0000
+Subject: [PATCH 1/1] * Add firewall support, v2
+
+---
+ configure.in | 6 +-
+ doc/bird.sgml | 34 ++++
+ nest/proto.c | 3 +
+ nest/protocol.h | 2 +-
+ nest/route.h | 3 +-
+ proto/firewall/Doc | 1 +
+ proto/firewall/Makefile | 6 +
+ proto/firewall/config.Y | 77 +++++++++
+ proto/firewall/firewall.c | 198 ++++++++++++++++++++++
+ proto/firewall/firewall.h | 54 ++++++
+ sysdep/autoconf.h.in | 5 +
+ sysdep/bsd/Modules | 1 +
+ sysdep/bsd/fw.c | 404 +++++++++++++++++++++++++++++++++++++++++++++
+ 13 files changed, 791 insertions(+), 3 deletions(-)
+ create mode 100644 proto/firewall/Doc
+ create mode 100644 proto/firewall/Makefile
+ create mode 100644 proto/firewall/config.Y
+ create mode 100644 proto/firewall/firewall.c
+ create mode 100644 proto/firewall/firewall.h
+ create mode 100644 sysdep/bsd/fw.c
+
+diff --git configure.in configure.in
+index 46a6ecd..bb5f445 100644
+--- configure.in
++++ configure.in
+@@ -47,7 +47,7 @@ if test "$enable_ipv6" = yes ; then
+ else
+ ip=ipv4
+ SUFFIX6=""
+- all_protocols=bgp,ospf,pipe,rip,static
++ all_protocols=bgp,ospf,pipe,rip,static,firewall
+ fi
+
+ if test "$with_protocols" = all ; then
+@@ -126,10 +126,13 @@ else
+ ipv4:netbsd*) sysdesc=bsd
+ CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
+ LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
++ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
+ ;;
+ ipv6:freebsd*) sysdesc=bsd-v6
+ ;;
+ ipv4:freebsd*) sysdesc=bsd
++ AC_DEFINE(CONFIG_FIREWALL_IPFW, 1)
++ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
+ ;;
+ ipv6:kfreebsd*) sysdesc=bsd-v6
+ ;;
+@@ -138,6 +141,7 @@ else
+ ipv6:openbsd*) sysdesc=bsd-v6
+ ;;
+ ipv4:openbsd*) sysdesc=bsd
++ AC_DEFINE(CONFIG_FIREWALL_PF, 1)
+ ;;
+ *) AC_MSG_ERROR([Cannot determine correct system configuration. Please use --with-sysconfig to set it manually.])
+ ;;
+--- configure.orig 2012-01-20 21:04:39.000000000 +0400
++++ configure 2012-01-26 17:37:43.000000000 +0400
+@@ -2336,7 +2336,7 @@
+ else
+ ip=ipv4
+ SUFFIX6=""
+- all_protocols=bgp,ospf,pipe,rip,static
++ all_protocols=bgp,ospf,pipe,rip,static,firewall
+ fi
+
+ if test "$with_protocols" = all ; then
+@@ -4372,10 +4372,13 @@
+ ipv4:netbsd*) sysdesc=bsd
+ CPPFLAGS="$CPPFLAGS -I/usr/pkg/include"
+ LDFLAGS="$LDFLAGS -L/usr/pkg/lib -R/usr/pkg/lib"
++ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
+ ;;
+ ipv6:freebsd*) sysdesc=bsd-v6
+ ;;
+ ipv4:freebsd*) sysdesc=bsd
++ $as_echo "#define CONFIG_FIREWALL_IPFW 1" >>confdefs.h
++ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
+ ;;
+ ipv6:kfreebsd*) sysdesc=bsd-v6
+ ;;
+@@ -4384,6 +4387,7 @@
+ ipv6:openbsd*) sysdesc=bsd-v6
+ ;;
+ ipv4:openbsd*) sysdesc=bsd
++ $as_echo "#define CONFIG_FIREWALL_PF 1" >>confdefs.h
+ ;;
+ *) as_fn_error $? "Cannot determine correct system configuration. Please use --with-sysconfig to set it manually." "$LINENO" 5
+ ;;--- doc/bird.sgml
++++ doc/bird.sgml
+@@ -2490,6 +2490,40 @@ protocol static {
+ }
+ </code>
+
++<sect>Firewall
++
++<p>Firewall protocol doesn't communicate with any network devices,
++but instead it allows you to add announced prefixes to given firewall table.
++At the moment IPFW and PF are supported. One can also specify special integer tag
++that can be passed as argument to IPFW table. Any number of instances can be configured.
++
++<p>Firewall protocol does not have many configuration options.
++
++<descrip>
++ <tag>fwtype pf|ipfw</tag> Select firewall type.
++ <tag>fwtable <m/name/</tag> Specifies firewall table name.
++ <tag>keep on startup|shutdown</tag>Do not flush table on protocol startup or shutdown.
++ <tag>keep always</tag>Do not flush table on protocol startup and shutdown.
++</descrip>
++
++<p>Firewall defines single route attribute:
++
++<descrip>
++ <tag>int <cf/fw_value/</tag> Value that can be passed with prefix.
++ Value is unsigned 4-byte integer. It can be set when importing routes from the other
++ protocols or on protocol export.
++</descrip>
++
++<p>Example firewall config might look like this:
++
++<p><code>
++protocol firewall {
++ table testable; # Connect to a non-default routing table
++ fwtype ipfw; # Use IPFW as backend
++ fwtable "2"; # Use table 2
++ export filter { fw_value = 125; accept; }; # Set value 125 for all prefixes
++}
++</code>
+ <chapt>Conclusions
+
+ <sect>Future work
+diff --git nest/proto.c nest/proto.c
+index d55c348..85bdb19 100644
+--- nest/proto.c
++++ nest/proto.c
+@@ -632,6 +632,9 @@ protos_build(void)
+ #ifdef CONFIG_BGP
+ proto_build(&proto_bgp);
+ #endif
++#ifdef CONFIG_FIREWALL
++ proto_build(&proto_firewall);
++#endif
+ proto_pool = rp_new(&root_pool, "Protocols");
+ proto_flush_event = ev_new(proto_pool);
+ proto_flush_event->hook = proto_flush_all;
+diff --git nest/protocol.h nest/protocol.h
+index a7518c2..d09a556 100644
+--- nest/protocol.h
++++ nest/protocol.h
+@@ -73,7 +73,7 @@ void protos_dump_all(void);
+
+ extern struct protocol
+ proto_device, proto_radv, proto_rip, proto_static,
+- proto_ospf, proto_pipe, proto_bgp;
++ proto_ospf, proto_pipe, proto_bgp, proto_firewall;
+
+ /*
+ * Routing Protocol Instance
+diff --git nest/route.h nest/route.h
+index a4c0154..e5f18dd 100644
+--- nest/route.h
++++ nest/route.h
+@@ -349,7 +349,8 @@ typedef struct eattr {
+ #define EAP_RIP 2 /* RIP */
+ #define EAP_OSPF 3 /* OSPF */
+ #define EAP_KRT 4 /* Kernel route attributes */
+-#define EAP_MAX 5
++#define EAP_FIREWALL 5 /* Abstact firewall interface */
++#define EAP_MAX 6
+
+ #define EA_CODE(proto,id) (((proto) << 8) | (id))
+ #define EA_PROTO(ea) ((ea) >> 8)
+diff --git proto/firewall/Doc proto/firewall/Doc
+new file mode 100644
+index 0000000..5779342
+--- /dev/null
++++ proto/firewall/Doc
+@@ -0,0 +1 @@
++S firewall.c
+diff --git proto/firewall/Makefile proto/firewall/Makefile
+new file mode 100644
+index 0000000..a322ab6
+--- /dev/null
++++ proto/firewall/Makefile
+@@ -0,0 +1,6 @@
++source=firewall.c
++root-rel=../../
++dir-name=proto/firewall
++
++include ../../Rules
++
+diff --git proto/firewall/config.Y proto/firewall/config.Y
+new file mode 100644
+index 0000000..aefc606
+--- /dev/null
++++ proto/firewall/config.Y
+@@ -0,0 +1,77 @@
++/*
++ * BIRD -- Firewall Protocol Configuration
++ *
++ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
++ *
++ * Can be freely distributed and used under the terms of the GNU GPL.
++ */
++
++CF_HDR
++
++#include "proto/firewall/firewall.h"
++
++CF_DEFINES
++
++#define FIREWALL_CFG ((struct firewall_config *) this_proto)
++
++CF_DECLS
++
++CF_KEYWORDS(FIREWALL, FWTABLE, FWTYPE, FW_VALUE, IPFW, PF, IPSET, KEEP, ON, STARTUP, SHUTDOWN, ALWAYS)
++
++%type <i> firewall_type
++CF_GRAMMAR
++
++CF_ADDTO(proto, firewall_proto '}')
++
++firewall_proto_start: proto_start FIREWALL {
++ this_proto = proto_config_new(&proto_firewall, sizeof(struct firewall_config), $1);
++ this_proto->preference = 0;
++ FIREWALL_CFG->flush_start = 1;
++ FIREWALL_CFG->flush_shutdown = 1;
++ }
++ ;
++
++firewall_proto:
++ firewall_proto_start proto_name '{'
++ | firewall_proto proto_item ';'
++ | firewall_proto firewall_proto_item ';'
++ ;
++
++firewall_proto_item:
++ FWTYPE firewall_type {
++ switch ($2)
++ {
++#ifdef CONFIG_FIREWALL_IPFW
++ case FWTYPE_IPFW:
++ break;
++#endif
++#ifdef CONFIG_FIREWALL_PF
++ case FWTYPE_PF:
++ break;
++#endif
++#ifdef CONFIG_FIREWALL_IPSET
++ case FWTYPE_IPSET:
++ break;
++#endif
++ default:
++ cf_error("firewall type is not supported by your OS/build");
++ }
++ FIREWALL_CFG->fwtype = $2;
++ };
++ | FWTABLE TEXT { FIREWALL_CFG->fwtable = $2; }
++ | KEEP ON STARTUP { FIREWALL_CFG->flush_start = 0; }
++ | KEEP ON SHUTDOWN { FIREWALL_CFG->flush_shutdown = 0; }
++ | KEEP ALWAYS { FIREWALL_CFG->flush_start = 0; FIREWALL_CFG->flush_shutdown = 0; }
++ ;
++
++firewall_type:
++ IPFW { $$ = FWTYPE_IPFW; }
++ | PF { $$ = FWTYPE_PF; }
++ | IPSET { $$ = FWTYPE_IPSET; }
++ ;
++
++CF_ADDTO(dynamic_attr, FW_VALUE { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_FIREWALL_VALUE); })
++
++CF_CODE
++
++CF_END
+diff --git proto/firewall/firewall.c proto/firewall/firewall.c
+new file mode 100644
+index 0000000..e447470
+--- /dev/null
++++ proto/firewall/firewall.c
+@@ -0,0 +1,198 @@
++/*
++ * BIRD -- Firewall Protocol Configuration
++ *
++ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
++ *
++ * Can be freely distributed and used under the terms of the GNU GPL.
++ */
++
++/**
++ * DOC: Firewall
++ *
++ * Firewall protocol is very simple. It adds or removes exported routes to given firewall
++ * table with zero (or filter-specified) value. Table can be flushed on startup to
++ * avoid error messages on bird restart.
++ */
++
++#undef LOCAL_DEBUG
++
++#include "nest/bird.h"
++#include "nest/iface.h"
++#include "nest/protocol.h"
++#include "nest/route.h"
++#include "conf/conf.h"
++#include "filter/filter.h"
++#include "lib/string.h"
++
++#include "firewall.h"
++
++static int init_done = 0;
++struct rate_limit rl_fw_err;
++
++static void
++firewall_collect(void)
++{
++ memset(&firewalls, 0, sizeof(firewalls));
++ log(L_DEBUG "Initializing firewalls..");
++#ifdef CONFIG_FIREWALL_IPFW
++ firewalls[FWTYPE_IPFW] = &fw_ipfw;
++ log(L_DEBUG "IPFW..");
++#endif
++#ifdef CONFIG_FIREWALL_PF
++ firewalls[FWTYPE_PF] = &fw_pf;
++ log(L_DEBUG "PF..");
++#endif
++#ifdef CONFIG_FIREWALL_IPSET
++ firewalls[FWTYPE_IPSET] = &fw_ipset;
++ log(L_DEBUG "IPSET..");
++#endif
++}
++
++static void
++firewall_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
++{
++ struct firewall_proto *p = (struct firewall_proto *) P;
++ u32 prefix_val;
++ char prefix_data[10];
++
++ if (!new && !old)
++ return;
++
++ prefix_val = ea_get_int(attrs, EA_FIREWALL_VALUE, 0);
++
++ if (prefix_val)
++ bsnprintf(prefix_data, sizeof(prefix_data), "%u", prefix_val);
++ else
++ prefix_data[0] = '\0';
++
++ DBG("Got prefix %I/%d with data '%s'\n", n->n.prefix, n->n.pxlen, prefix_data);
++
++ if (old && new && p->fw->fw_replace)
++ {
++ p->fw->fw_replace(p->fwdata, n, prefix_data);
++ return;
++ }
++
++ if (old)
++ p->fw->fw_del(p->fwdata, n);
++
++ if (new)
++ p->fw->fw_add(p->fwdata, n, prefix_data);
++}
++
++static int
++firewall_start(struct proto *P)
++{
++ struct firewall_proto *p = (struct firewall_proto *) P;
++ struct firewall_config *c = (struct firewall_config *)P->cf;
++ void *fwdata;
++
++ if ((fwdata = p->fw->fw_init(P, c->fwtable)) == NULL)
++ return PS_START;
++
++ p->fwdata = fwdata;
++
++ /* Flush table if needed */
++ if ((c->flush_start) && (p->fw->fw_flush))
++ if (!p->fw->fw_flush(fwdata))
++ {
++ log(L_ERR "flush failed for table %s", c->fwtable);
++ return PS_START;
++ }
++
++ return PS_UP;
++}
++
++static int
++firewall_shutdown(struct proto *P)
++{
++ struct firewall_proto *p = (struct firewall_proto *) P;
++ struct firewall_config *c = (struct firewall_config *)P->cf;
++
++ log(L_DEBUG, "Shutdown requested");
++
++ /* Flush table if needed */
++ if ((c->flush_shutdown) && (p->fw->fw_flush))
++ if (!p->fw->fw_flush(p->fwdata))
++ log(L_ERR "flush failed for table %s", c->fwtable);
++
++ p->fw->fw_shutdown(p->fwdata);
++
++ return PS_DOWN;
++}
++
++static struct proto *
++firewall_init(struct proto_config *C)
++{
++ struct firewall_config *c = (struct firewall_config *) C;
++ struct proto *P = proto_new(C, sizeof(struct firewall_proto));
++ struct firewall_proto *p = (struct firewall_proto *) P;
++
++ /* Configure firewalls */
++ if (!init_done)
++ {
++ init_done = 1;
++ firewall_collect();
++ }
++
++ p->fwtype = c->fwtype;
++ p->fw = firewalls[p->fwtype];
++ P->accept_ra_types = RA_OPTIMAL;
++ P->rt_notify = firewall_rt_notify;
++
++ return P;
++}
++
++static int
++firewall_reconfigure(struct proto *P, struct proto_config *new)
++{
++ struct firewall_config *o = (struct firewall_config *) P->cf;
++ struct firewall_config *n = (struct firewall_config *) new;
++
++ if ((o->fwtype != n->fwtype) || (strcmp(o->fwtable, n->fwtable)))
++ return 0;
++
++ return 1;
++}
++
++static void
++firewall_copy_config(struct proto_config *dest, struct proto_config *src)
++{
++ /* Just a shallow copy, not many items here */
++ proto_copy_rest(dest, src, sizeof(struct firewall_config));
++}
++
++static void
++firewall_get_status(struct proto *P, byte *buf)
++{
++ struct firewall_config *c = (struct firewall_config *) P->cf;
++
++ bsprintf(buf, "Table [%s]", c->fwtable);
++}
++
++static int
++firewall_get_attr(eattr * a, byte * buf, int buflen UNUSED)
++{
++ switch (a->id)
++ {
++ case EA_FIREWALL_VALUE:
++ bsprintf(buf, "fw_value");
++ return GA_NAME;
++ default:
++ return GA_UNKNOWN;
++ }
++}
++
++
++struct protocol proto_firewall = {
++ name: "Firewall",
++ template: "fw%d",
++ attr_class: EAP_FIREWALL,
++ init: firewall_init,
++ start: firewall_start,
++ shutdown: firewall_shutdown,
++ reconfigure: firewall_reconfigure,
++ copy_config: firewall_copy_config,
++ get_status: firewall_get_status,
++ get_attr: firewall_get_attr,
++};
+diff --git proto/firewall/firewall.h proto/firewall/firewall.h
+new file mode 100644
+index 0000000..c97ed38
+--- /dev/null
++++ proto/firewall/firewall.h
+@@ -0,0 +1,54 @@
++/*
++ * BIRD -- Firewall Protocol Configuration
++ *
++ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
++ *
++ * Can be freely distributed and used under the terms of the GNU GPL.
++ */
++
++#ifndef _BIRD_FIREWALL_H_
++#define _BIRD_FIREWALL_H_
++
++#define FWTYPE_IPFW 0
++#define FWTYPE_PF 1
++#define FWTYPE_IPSET 2
++
++#define FWTYPE_MAX 3
++
++#define EA_FIREWALL_VALUE EA_CODE(EAP_FIREWALL, 0)
++
++struct firewall_config {
++ struct proto_config c;
++ int fwtype; /* Firewall type */
++ char *fwtable; /* Firewall table to write to */
++ int flush_start; /* Do table flush on startup? */
++ int flush_shutdown; /* Do table flush on shutdown? */
++};
++
++struct firewall_control {
++ int fwtype; /* Firewall type */
++ char *description; /* Firewall description */
++ void *(*fw_init)(struct proto *, char *); /* Init firewall instance */
++ void (*fw_shutdown)(void *); /* Shutdown firewall instance */
++ int (*fw_flush)(void *); /* Flush firewall table */
++ int (*fw_add)(void *, net *, char *); /* Add record to table */
++ int (*fw_del)(void *, net *); /* Remove record from table */
++ int (*fw_replace)(void *, net *, char *); /* Replace record. Optional */
++};
++
++struct firewall_control * firewalls[FWTYPE_MAX];
++
++struct firewall_proto {
++ struct proto p;
++ int fwtype; /* Firewall type */
++ struct firewall_control *fw; /* Pointer to configured protocol type */
++ void *fwdata; /* Firewall instance private data */
++};
++
++extern struct protocol proto_firewall;
++
++extern struct firewall_control fw_ipfw, fw_pf, fw_ipset;
++extern struct rate_limit rl_fw_err;
++#define FW_ERR(x, y...) log_rl(&rl_fw_err, L_ERR x, ##y)
++
++#endif
+diff --git sysdep/autoconf.h.in sysdep/autoconf.h.in
+index d029e2a..c1fcdf7 100644
+--- sysdep/autoconf.h.in
++++ sysdep/autoconf.h.in
+@@ -42,6 +42,11 @@
+ #undef CONFIG_BGP
+ #undef CONFIG_OSPF
+ #undef CONFIG_PIPE
++#undef CONFIG_FIREWALL
++
++#undef CONFIG_FIREWALL_IPFW
++#undef CONFIG_FIREWALL_PF
++#undef CONFIG_FIREWALL_IPSET
+
+ /* We have <syslog.h> and syslog() */
+ #undef HAVE_SYSLOG
+diff --git sysdep/bsd/Modules sysdep/bsd/Modules
+index 84abffd..77f26e3 100644
+--- sysdep/bsd/Modules
++++ sysdep/bsd/Modules
+@@ -4,3 +4,4 @@ sysio.h
+ krt-set.h
+ krt-sock.c
+ krt-sock.h
++fw.c
+diff --git sysdep/bsd/fw.c sysdep/bsd/fw.c
+new file mode 100644
+index 0000000..e841e06
+--- /dev/null
++++ sysdep/bsd/fw.c
+@@ -0,0 +1,404 @@
++/*
++ * BIRD -- IPFW/PF manipulations
++ *
++ * (c) 2011 Alexander V. Chernikov <melifaro@FreeBSD.org>
++ *
++ * Can be freely distributed and used under the terms of the GNU GPL.
++ */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <limits.h>
++#include <ctype.h>
++#include <fcntl.h>
++#include <unistd.h>
++#include <sys/param.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <sys/sysctl.h>
++#include <sys/ioctl.h>
++#include <netinet/in.h>
++#include <errno.h>
++#include <err.h>
++#include <net/route.h>
++#include <net/if.h>
++#include <net/if_dl.h>
++
++#undef LOCAL_DEBUG
++
++#include "nest/bird.h"
++#include "nest/iface.h"
++#include "nest/route.h"
++#include "nest/protocol.h"
++#include "nest/iface.h"
++#include "lib/timer.h"
++#include "lib/unix.h"
++#include "lib/krt.h"
++#include "lib/string.h"
++#include "lib/socket.h"
++#ifdef CONFIG_FIREWALL
++#include "proto/firewall/firewall.h"
++#ifdef CONFIG_FIREWALL_IPFW
++#include "netinet/ip_fw.h"
++#endif
++#ifdef CONFIG_FIREWALL_PF
++#include "net/pfvar.h"
++#endif
++
++#ifdef CONFIG_FIREWALL_IPFW
++
++int ipfw_fd = -1;
++int ipfw_instance_count = 0;
++
++struct ipfw_priv {
++ int table; /* Table number */
++ pool *pool; /* Protocol pool */
++};
++
++int
++ipfw_do_cmd(int optname, void *optval, uintptr_t optlen)
++{
++ return setsockopt(ipfw_fd, IPPROTO_IP, optname, optval, optlen);
++}
++
++void *
++ipfw_fw_init(struct proto *p, char *table)
++{
++ pool *fwpool = p->pool;
++ int table_num = strtol(table, NULL, 10);
++ int tables_max;
++ size_t len = sizeof(tables_max);
++
++ if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, NULL, 0) == -1)
++ {
++ log(L_ERR "Error getting maximum ipfw table count");
++ tables_max = IPFW_TABLES_MAX;
++ }
++ DBG("ipfw maximum table count set to %d\n", tables_max);
++
++ if ((table_num < 0) || (table_num >= tables_max))
++ {
++ log(L_ERR "ipfw table %d is not within possible range (0..%d)", table_num, tables_max);
++ return NULL;
++ }
++
++ if (ipfw_fd == -1)
++ {
++ if ((ipfw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
++ {
++ log(L_ERR "ipfw: error opering raw socket: %m");
++ return NULL;
++ }
++ DBG("Opened IPFW socked %d\n", ipfw_fd);
++ }
++
++ struct ipfw_priv *priv = mb_alloc(fwpool, sizeof(struct ipfw_priv));
++
++ priv->table = table_num;
++ priv->pool = fwpool;
++
++ ipfw_instance_count++;
++
++ return priv;
++}
++
++void
++ipfw_fw_shutdown(void *_priv UNUSED)
++{
++ if (--ipfw_instance_count == 0)
++ {
++ DBG("Closing ipfw socket %d\n", ipfw_fd);
++ close(ipfw_fd);
++ ipfw_fd = -1;
++ }
++}
++
++int
++ipfw_fw_flush(void *_priv)
++{
++ struct ipfw_priv *priv = _priv;
++ ipfw_table_entry ent;
++
++ memset(&ent, 0, sizeof(ent));
++ ent.tbl = priv->table;
++
++ log(L_DEBUG "Flushing ipfw table %d", priv->table);
++
++ if (ipfw_do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) == -1)
++ {
++ log(L_ERR "Error flushing ipfw table %d: %m", priv->table);
++ return 0;
++ }
++
++ return 1;
++}
++
++int
++ipfw_fw_add(void *_priv, net *n, char *prefixdata)
++{
++ struct ipfw_priv *priv = _priv;
++ ip_addr addr;
++ ipfw_table_entry ent;
++
++ addr = n->n.prefix;
++ ipa_hton(addr);
++
++ ent.masklen = n->n.pxlen;
++ memcpy(&ent.addr, &addr, sizeof(ip_addr));
++ ent.value = strtol(prefixdata, NULL, 0);
++ ent.tbl = priv->table;
++
++ DBG("Adding %I/%d to ipfw table %d with value %s\n", n->n.prefix, n->n.pxlen, priv->table, prefixdata);
++
++ if (ipfw_do_cmd(IP_FW_TABLE_ADD, &ent, sizeof(ent)) == -1)
++ {
++ FW_ERR("Error adding %I/%d to ipfw table %d: %m", n->n.prefix, n->n.pxlen, priv->table);
++ return 0;
++ }
++
++ return 1;
++}
++
++int
++ipfw_fw_del(void *_priv, net *n)
++{
++ struct ipfw_priv *priv = _priv;
++ ip_addr addr;
++ ipfw_table_entry ent;
++
++ addr = n->n.prefix;
++ ipa_hton(addr);
++
++ ent.masklen = n->n.pxlen;
++ memcpy(&ent.addr, &addr, sizeof(ip_addr));
++ ent.value = 0;
++ ent.tbl = priv->table;
++
++ DBG("Removing %I/%d from ipfw table %d\n", n->n.prefix, n->n.pxlen, priv->table);
++
++ if (ipfw_do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent)) == -1)
++ {
++ FW_ERR("Error removing %I/%d from ipfw table %d: %m", n->n.prefix, n->n.pxlen, priv->table);
++ return 0;
++ }
++
++ return 1;
++}
++
++struct firewall_control fw_ipfw = {
++ fwtype: FWTYPE_IPFW,
++ description: "IPFW",
++ fw_init: ipfw_fw_init,
++ fw_shutdown: ipfw_fw_shutdown,
++ fw_flush: ipfw_fw_flush,
++ fw_add: ipfw_fw_add,
++ fw_del: ipfw_fw_del,
++};
++#endif
++
++#ifdef CONFIG_FIREWALL_PF
++
++#define PF_DEVNAME "/dev/pf"
++int pf_fd = -1;
++int pf_instance_count = 0;
++
++struct pf_priv {
++ struct pfr_table table; /* PF table structure */
++ pool *pool; /* Protocol pool */
++};
++
++#define pf_tablename table.pfrt_name
++
++int
++pf_do_cmd(struct pfr_table *tbl, unsigned long cmd, void *buffer, int esize, int items, int *nadd, int *ndel, int flags)
++{
++ struct pfioc_table io;
++
++ bzero(&io, sizeof(io));
++ io.pfrio_flags = flags;
++ if (tbl)
++ io.pfrio_table = *tbl;
++ io.pfrio_buffer = buffer;
++ io.pfrio_esize = esize;
++ io.pfrio_size = items;
++
++ /* DBG("Doing PF ioctl %X for table %s on fd %d\n", cmd, tbl ? tbl->pfrt_name : "NULL", pf_fd); */
++ if (ioctl(pf_fd, cmd, &io))
++ return 0;
++
++ if (nadd)
++ *nadd = io.pfrio_nadd;
++ if (ndel)
++ *ndel = io.pfrio_ndel;
++
++ return 1;
++}
++
++void *
++pf_fw_init(struct proto *p, char *table)
++{
++ pool *fwpool = p->pool;
++ struct pfr_table pf_table;
++ int nadd = 0;
++
++ if (strlen(table) > PF_TABLE_NAME_SIZE)
++ {
++ log(L_ERR "PF table name too long, max %d", PF_TABLE_NAME_SIZE);
++ return NULL;
++ }
++
++ memset(&pf_table, 0, sizeof(pf_table));
++
++ if (pf_fd == -1)
++ {
++ if ((pf_fd = open(PF_DEVNAME, O_RDWR)) == -1)
++ {
++ log(L_ERR "pf: error opening %s: %m", PF_DEVNAME);
++ return NULL;
++ }
++
++ DBG("Opened PF socked %d\n", pf_fd);
++ }
++
++ strcpy(pf_table.pfrt_name, table);
++ pf_table.pfrt_flags |= PFR_TFLAG_PERSIST;
++ if (!pf_do_cmd(NULL, DIOCRADDTABLES, &pf_table, sizeof(pf_table), 1, &nadd, NULL, 0))
++ {
++ log(L_ERR "Error creating PF table %s: %m", table);
++ if (pf_instance_count == 0)
++ {
++ log(L_ERR "Closing PF socket");
++ close(pf_fd);
++ pf_fd = -1;
++ }
++ return NULL;
++ }
++ DBG("PF table %s created\n", table);
++ /* Remove persistent flag */
++ pf_table.pfrt_flags = 0;
++
++ struct pf_priv *priv = mb_alloc(fwpool, sizeof(struct pf_priv));
++
++ priv->table = pf_table;
++ priv->pool = fwpool;
++
++ pf_instance_count++;
++
++ return priv;
++}
++
++void
++pf_fw_shutdown(void *_priv UNUSED)
++{
++ if (--pf_instance_count == 0)
++ {
++ DBG("Closing PF socket %d\n", pf_fd);
++ close(pf_fd);
++ pf_fd = -1;
++ }
++}
++
++int
++pf_fw_flush(void *_priv)
++{
++ struct pf_priv *priv = _priv;
++ int ndel;
++
++ log(L_DEBUG "Flushing PF table %s", priv->pf_tablename);
++
++ if (!pf_do_cmd(&priv->table, DIOCRCLRADDRS, NULL, 0, 0, NULL, &ndel, 0))
++ {
++ log(L_ERR "Error flushing PF table %s: %m", priv->pf_tablename);
++ return 0;
++ }
++
++ DBG("Flushed %d record(s) from PF table %s\n", ndel, priv->pf_tablename);
++
++ return 1;
++}
++
++static int
++pf_put_addr(struct pfr_addr *pf_addr, net *n)
++{
++ int rt_family = AF_INET;
++ ip_addr addr;
++
++ memset(pf_addr, 0, sizeof(struct pfr_addr));
++ pf_addr->pfra_not = 0;
++ pf_addr->pfra_net = n->n.pxlen;
++ switch (rt_family)
++ {
++ case AF_INET:
++ addr = n->n.prefix;
++ ipa_hton(addr);
++ pf_addr->pfra_ip4addr.s_addr = addr;
++ pf_addr->pfra_af = rt_family;
++ break;
++ default:
++ log(L_ERR "Address family %d is not supported by pf, ignoring prefix", rt_family);
++ return 0;
++ }
++
++ return 1;
++}
++
++int
++pf_fw_add(void *_priv, net *n, char *prefixdata)
++{
++ struct pf_priv *priv = _priv;
++ struct pfr_addr pf_addr;
++ int nadd = 0;
++
++ if (!pf_put_addr(&pf_addr, n))
++ {
++ FW_ERR("Error adding %I/%d to PF table %s", n->n.prefix, n->n.pxlen, priv->pf_tablename);
++ return 0;
++ }
++
++ DBG("Adding %I/%d to PF table %s with value %s\n", n->n.prefix, n->n.pxlen, priv->pf_tablename, prefixdata);
++ if (!pf_do_cmd(&priv->table, DIOCRADDADDRS, &pf_addr, sizeof(pf_addr), 1, &nadd, NULL, 0))
++ {
++ FW_ERR("Error adding %I/%d to PF table %s: %m", n->n.prefix, n->n.pxlen, priv->pf_tablename);
++ return 0;
++ }
++
++ return 1;
++}
++
++int
++pf_fw_del(void *_priv, net *n)
++{
++ struct pf_priv *priv = _priv;
++ struct pfr_addr pf_addr;
++ int ndel = 0;
++
++ if (!pf_put_addr(&pf_addr, n))
++ {
++ FW_ERR("Error deleting %I/%d from PF table %s", n->n.prefix, n->n.pxlen, priv->pf_tablename);
++ return 0;
++ }
++
++ DBG("Deleting %I/%d from PF table %s\n", n->n.prefix, n->n.pxlen, priv->pf_tablename);
++ if (!pf_do_cmd(&priv->table, DIOCRDELADDRS, &pf_addr, sizeof(pf_addr), 1, NULL, &ndel, 0))
++ {
++ FW_ERR("Error deleting %I/%d from PF table %s: %m", n->n.prefix, n->n.pxlen, priv->pf_tablename);
++ return 0;
++ }
++
++ return 1;
++}
++
++struct firewall_control fw_pf = {
++ fwtype: FWTYPE_PF,
++ description: "PF",
++ fw_init: pf_fw_init,
++ fw_shutdown: pf_fw_shutdown,
++ fw_flush: pf_fw_flush,
++ fw_add: pf_fw_add,
++ fw_del: pf_fw_del,
++};
++#endif
++
++
++#endif
++
+--
+1.7.3.2
+
diff --git a/net/bird/files/patch-136-post.diff b/net/bird/files/patch-136-post.diff
new file mode 100644
index 000000000000..0bc99258965c
--- /dev/null
+++ b/net/bird/files/patch-136-post.diff
@@ -0,0 +1,507 @@
+diff --git nest/config.Y nest/config.Y
+index a6baf4e..3fcfa52 100644
+--- nest/config.Y
++++ nest/config.Y
+@@ -219,7 +219,6 @@ CF_ADDTO(proto, dev_proto '}')
+
+ dev_proto_start: proto_start DIRECT {
+ this_proto = proto_config_new(&proto_device, sizeof(struct rt_dev_config), $1);
+- this_proto->preference = DEF_PREF_DIRECT;
+ init_list(&DIRECT_CFG->iface_list);
+ }
+ ;
+diff --git nest/iface.c nest/iface.c
+index b8b214e..eea3d3b 100644
+--- nest/iface.c
++++ nest/iface.c
+@@ -253,6 +253,24 @@ if_change_flags(struct iface *i, unsigned flags)
+ }
+
+ /**
++ * if_delete - remove interface
++ * @old: interface
++ *
++ * This function is called by the low-level platform dependent code
++ * whenever it notices an interface disappears. It is just a shorthand
++ * for if_update().
++ */
++
++void
++if_delete(struct iface *old)
++{
++ struct iface f = {};
++ strncpy(f.name, old->name, sizeof(f.name)-1);
++ f.flags = IF_SHUTDOWN;
++ if_update(&f);
++}
++
++/**
+ * if_update - update interface status
+ * @new: new interface status
+ *
+@@ -288,13 +306,14 @@ if_update(struct iface *new)
+ new->addr = i->addr;
+ memcpy(&new->addrs, &i->addrs, sizeof(i->addrs));
+ memcpy(i, new, sizeof(*i));
++ i->flags &= ~IF_UP; /* IF_TMP_DOWN will be added later */
+ goto newif;
+ }
+- else if (c)
+- {
+- if_copy(i, new);
+- if_notify_change(c, i);
+- }
++
++ if_copy(i, new);
++ if (c)
++ if_notify_change(c, i);
++
+ i->flags |= IF_UPDATED;
+ return i;
+ }
+@@ -400,7 +419,7 @@ if_find_by_index(unsigned idx)
+ struct iface *i;
+
+ WALK_LIST(i, iface_list)
+- if (i->index == idx)
++ if (i->index == idx && !(i->flags & IF_SHUTDOWN))
+ return i;
+ return NULL;
+ }
+diff --git nest/iface.h nest/iface.h
+index d6d58ff..2416f82 100644
+--- nest/iface.h
++++ nest/iface.h
+@@ -88,6 +88,7 @@ void ifa_dump(struct ifa *);
+ void if_show(void);
+ void if_show_summary(void);
+ struct iface *if_update(struct iface *);
++void if_delete(struct iface *old);
+ struct ifa *ifa_update(struct ifa *);
+ void ifa_delete(struct ifa *);
+ void if_start_update(void);
+diff --git nest/proto.c nest/proto.c
+index d55c348..0fc72ce 100644
+--- nest/proto.c
++++ nest/proto.c
+@@ -200,6 +200,7 @@ proto_config_new(struct protocol *pr, unsigned size, int class)
+ c->global = new_config;
+ c->protocol = pr;
+ c->name = pr->name;
++ c->preference = pr->preference;
+ c->class = class;
+ c->out_filter = FILTER_REJECT;
+ c->table = c->global->master_rtc;
+diff --git nest/protocol.h nest/protocol.h
+index 3766e15..a83c4ff 100644
+--- nest/protocol.h
++++ nest/protocol.h
+@@ -39,6 +39,7 @@ struct protocol {
+ char *template; /* Template for automatic generation of names */
+ int name_counter; /* Counter for automatic name generation */
+ int attr_class; /* Attribute class known to this protocol */
++ unsigned preference; /* Default protocol preference */
+
+ void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
+ void (*postconfig)(struct proto_config *); /* After configuring each instance */
+diff --git nest/rt-dev.c nest/rt-dev.c
+index 497ee80..54cb14b 100644
+--- nest/rt-dev.c
++++ nest/rt-dev.c
+@@ -109,6 +109,7 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
+ struct protocol proto_device = {
+ name: "Direct",
+ template: "direct%d",
++ preference: DEF_PREF_DIRECT,
+ init: dev_init,
+ reconfigure: dev_reconfigure,
+ copy_config: dev_copy_config
+diff --git proto/bgp/bgp.c proto/bgp/bgp.c
+index 66fdc60..4d3c32f 100644
+--- proto/bgp/bgp.c
++++ proto/bgp/bgp.c
+@@ -1178,6 +1178,7 @@ struct protocol proto_bgp = {
+ name: "BGP",
+ template: "bgp%d",
+ attr_class: EAP_BGP,
++ preference: DEF_PREF_BGP,
+ init: bgp_init,
+ start: bgp_start,
+ shutdown: bgp_shutdown,
+diff --git proto/bgp/config.Y proto/bgp/config.Y
+index 5fb6094..78ca52d 100644
+--- proto/bgp/config.Y
++++ proto/bgp/config.Y
+@@ -33,7 +33,6 @@ CF_ADDTO(proto, bgp_proto '}' { bgp_check_config(BGP_CFG); } )
+
+ bgp_proto_start: proto_start BGP {
+ this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config), $1);
+- this_proto->preference = DEF_PREF_BGP;
+ BGP_CFG->hold_time = 240;
+ BGP_CFG->connect_retry_time = 120;
+ BGP_CFG->initial_hold_time = 240;
+diff --git proto/ospf/config.Y proto/ospf/config.Y
+index 4ada41e..24e125a 100644
+--- proto/ospf/config.Y
++++ proto/ospf/config.Y
+@@ -129,7 +129,6 @@ CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } )
+
+ ospf_proto_start: proto_start OSPF {
+ this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config), $1);
+- this_proto->preference = DEF_PREF_OSPF;
+ init_list(&OSPF_CFG->area_list);
+ init_list(&OSPF_CFG->vlink_list);
+ OSPF_CFG->rfc1583 = DEFAULT_RFC1583;
+diff --git proto/ospf/ospf.c proto/ospf/ospf.c
+index ce7ad37..73c06c2 100644
+--- proto/ospf/ospf.c
++++ proto/ospf/ospf.c
+@@ -1542,6 +1542,7 @@ struct protocol proto_ospf = {
+ name: "OSPF",
+ template: "ospf%d",
+ attr_class: EAP_OSPF,
++ preference: DEF_PREF_OSPF,
+ init: ospf_init,
+ dump: ospf_dump,
+ start: ospf_start,
+diff --git proto/ospf/ospf.h proto/ospf/ospf.h
+index 60a34fb..d696151 100644
+--- proto/ospf/ospf.h
++++ proto/ospf/ospf.h
+@@ -177,7 +177,7 @@ struct ospf_area_config
+ struct ospf_iface
+ {
+ node n;
+- struct iface *iface; /* Nest's iface */
++ struct iface *iface; /* Nest's iface, non-NULL (unless type OSPF_IT_VLINK) */
+ struct ifa *addr; /* IP prefix associated with that OSPF iface */
+ struct ospf_area *oa;
+ struct ospf_iface_patt *cf;
+diff --git proto/ospf/packet.c proto/ospf/packet.c
+index df67d5a..7a26967 100644
+--- proto/ospf/packet.c
++++ proto/ospf/packet.c
+@@ -489,17 +489,17 @@ ospf_rx_hook(sock *sk, int size)
+ void
+ ospf_tx_hook(sock * sk)
+ {
+-// struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
++ struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
+ // struct proto *p = (struct proto *) (ifa->oa->po);
+- log(L_ERR "OSPF: TX_Hook called");
++ log(L_ERR "OSPF: TX hook called on %s", ifa->iface->name);
+ }
+
+ void
+ ospf_err_hook(sock * sk, int err)
+ {
+-// struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
++ struct ospf_iface *ifa= (struct ospf_iface *) (sk->data);
+ // struct proto *p = (struct proto *) (ifa->oa->po);
+- log(L_ERR "OSPF: Socket error: %M", err);
++ log(L_ERR "OSPF: Socket error on %s: %M", ifa->iface->name, err);
+ }
+
+ void
+diff --git proto/pipe/config.Y proto/pipe/config.Y
+index 4478afe..4063755 100644
+--- proto/pipe/config.Y
++++ proto/pipe/config.Y
+@@ -24,7 +24,6 @@ CF_ADDTO(proto, pipe_proto '}')
+
+ pipe_proto_start: proto_start PIPE {
+ this_proto = proto_config_new(&proto_pipe, sizeof(struct pipe_config), $1);
+- this_proto->preference = DEF_PREF_PIPE;
+ PIPE_CFG->mode = PIPE_TRANSPARENT;
+ }
+ ;
+diff --git proto/pipe/pipe.c proto/pipe/pipe.c
+index 420c5a9..fe8618b 100644
+--- proto/pipe/pipe.c
++++ proto/pipe/pipe.c
+@@ -197,6 +197,7 @@ pipe_get_status(struct proto *P, byte *buf)
+ struct protocol proto_pipe = {
+ name: "Pipe",
+ template: "pipe%d",
++ preference: DEF_PREF_PIPE,
+ postconfig: pipe_postconfig,
+ init: pipe_init,
+ start: pipe_start,
+diff --git proto/rip/rip.c proto/rip/rip.c
+index 543aa30..f0a4134 100644
+--- proto/rip/rip.c
++++ proto/rip/rip.c
+@@ -975,7 +975,6 @@ void
+ rip_init_config(struct rip_proto_config *c)
+ {
+ init_list(&c->iface_list);
+- c->c.preference = DEF_PREF_RIP;
+ c->infinity = 16;
+ c->port = 520;
+ c->period = 30;
+@@ -1032,6 +1031,7 @@ struct protocol proto_rip = {
+ name: "RIP",
+ template: "rip%d",
+ attr_class: EAP_RIP,
++ preference: DEF_PREF_RIP,
+ get_route_info: rip_get_route_info,
+ get_attr: rip_get_attr,
+
+diff --git proto/static/static.c proto/static/static.c
+index f6c2a33..aaa9bfa 100644
+--- proto/static/static.c
++++ proto/static/static.c
+@@ -353,7 +353,6 @@ static_if_notify(struct proto *p, unsigned flags, struct iface *i)
+ void
+ static_init_config(struct static_config *c)
+ {
+- c->c.preference = DEF_PREF_STATIC;
+ init_list(&c->iface_routes);
+ init_list(&c->other_routes);
+ }
+@@ -523,6 +522,7 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
+ struct protocol proto_static = {
+ name: "Static",
+ template: "static%d",
++ preference: DEF_PREF_STATIC,
+ init: static_init,
+ dump: static_dump,
+ start: static_start,
+diff --git sysdep/bsd/krt-sock.c sysdep/bsd/krt-sock.c
+index 4bf6600..4ee5495 100644
+--- sysdep/bsd/krt-sock.c
++++ sysdep/bsd/krt-sock.c
+@@ -410,14 +410,42 @@ krt_read_rt(struct ks_msg *msg, struct krt_proto *p, int scan)
+ }
+
+ static void
++krt_read_ifannounce(struct ks_msg *msg)
++{
++ struct if_announcemsghdr *ifam = (struct if_announcemsghdr *)&msg->rtm;
++
++ if (ifam->ifan_what == IFAN_ARRIVAL)
++ {
++ /* Not enough info to create the iface, so we just trigger iface scan */
++ kif_request_scan();
++ }
++ else if (ifam->ifan_what == IFAN_DEPARTURE)
++ {
++ struct iface *iface = if_find_by_index(ifam->ifan_index);
++
++ /* Interface is destroyed */
++ if (!iface)
++ {
++ DBG("KRT: unknown interface (%s, #%d) going down. Ignoring\n", ifam->ifan_name, ifam->ifan_index);
++ return;
++ }
++
++ if_delete(iface);
++ }
++
++ DBG("KRT: IFANNOUNCE what: %d index %d name %s\n", ifam->ifan_what, ifam->ifan_index, ifam->ifan_name);
++}
++
++static void
+ krt_read_ifinfo(struct ks_msg *msg)
+ {
+ struct if_msghdr *ifm = (struct if_msghdr *)&msg->rtm;
+ void *body = (void *)(ifm + 1);
+ struct sockaddr_dl *dl = NULL;
+ unsigned int i;
+- struct iface *iface = NULL, f;
++ struct iface *iface = NULL, f = {};
+ int fl = ifm->ifm_flags;
++ int nlen = 0;
+
+ for (i = 1; i<=RTA_IFP; i <<= 1)
+ {
+@@ -432,31 +460,42 @@ krt_read_ifinfo(struct ks_msg *msg)
+ }
+ }
+
+- if(dl && (dl->sdl_family != AF_LINK))
++ if (dl && (dl->sdl_family != AF_LINK))
+ {
+- log("Ignoring strange IFINFO");
++ log(L_WARN "Ignoring strange IFINFO");
+ return;
+ }
+
+- iface = if_find_by_index(ifm->ifm_index);
++ if (dl)
++ nlen = MIN(sizeof(f.name)-1, dl->sdl_nlen);
+
+- if(!iface)
++ /* Note that asynchronous IFINFO messages do not contain iface
++ name, so we have to found an existing iface by iface index */
++
++ iface = if_find_by_index(ifm->ifm_index);
++ if (!iface)
+ {
+ /* New interface */
+- if(!dl) return; /* No interface name, ignoring */
++ if (!dl)
++ return; /* No interface name, ignoring */
+
+- bzero(&f, sizeof(f));
+- f.index = ifm->ifm_index;
+- memcpy(f.name, dl->sdl_data, MIN(sizeof(f.name)-1, dl->sdl_nlen));
+- DBG("New interface '%s' found", f.name);
++ memcpy(f.name, dl->sdl_data, nlen);
++ DBG("New interface '%s' found\n", f.name);
++ }
++ else if (dl && memcmp(iface->name, dl->sdl_data, nlen))
++ {
++ /* Interface renamed */
++ if_delete(iface);
++ memcpy(f.name, dl->sdl_data, nlen);
+ }
+ else
+ {
+- memcpy(&f, iface, sizeof(struct iface));
++ /* Old interface */
++ memcpy(f.name, iface->name, sizeof(f.name));
+ }
+
++ f.index = ifm->ifm_index;
+ f.mtu = ifm->ifm_data.ifi_mtu;
+- f.flags = 0;
+
+ if (fl & IFF_UP)
+ f.flags |= IF_ADMIN_UP;
+@@ -471,8 +510,7 @@ krt_read_ifinfo(struct ks_msg *msg)
+ else
+ f.flags |= IF_MULTIACCESS; /* NBMA */
+
+- if((!iface) || memcmp(&f, iface, sizeof(struct iface)))
+- if_update(&f); /* Just if something happens */
++ if_update(&f);
+ }
+
+ static void
+@@ -588,6 +626,9 @@ krt_read_msg(struct proto *p, struct ks_msg *msg, int scan)
+ case RTM_DELETE:
+ krt_read_rt(msg, (struct krt_proto *)p, scan);
+ break;
++ case RTM_IFANNOUNCE:
++ krt_read_ifannounce(msg);
++ break;
+ case RTM_IFINFO:
+ krt_read_ifinfo(msg);
+ break;
+diff --git sysdep/linux/netlink/netlink.c sysdep/linux/netlink/netlink.c
+index cf80823..17c369e 100644
+--- sysdep/linux/netlink/netlink.c
++++ sysdep/linux/netlink/netlink.c
+@@ -386,7 +386,7 @@ nl_parse_link(struct nlmsghdr *h, int scan)
+ struct ifinfomsg *i;
+ struct rtattr *a[IFLA_WIRELESS+1];
+ int new = h->nlmsg_type == RTM_NEWLINK;
+- struct iface f;
++ struct iface f = {};
+ struct iface *ifi;
+ char *name;
+ u32 mtu;
+@@ -408,26 +408,21 @@ nl_parse_link(struct nlmsghdr *h, int scan)
+ if (!new)
+ {
+ DBG("KIF: IF%d(%s) goes down\n", i->ifi_index, name);
+- if (ifi && !scan)
+- {
+- memcpy(&f, ifi, sizeof(struct iface));
+- f.flags |= IF_SHUTDOWN;
+- if_update(&f);
+- }
++ if (!ifi)
++ return;
++
++ if_delete(ifi);
+ }
+ else
+ {
+ 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
+- {
+- bzero(&f, sizeof(f));
+- f.index = i->ifi_index;
+- }
+- strncpy(f.name, RTA_DATA(a[IFLA_IFNAME]), sizeof(f.name)-1);
++ if (ifi && strncmp(ifi->name, name, sizeof(ifi->name)-1))
++ if_delete(ifi);
++
++ strncpy(f.name, name, sizeof(f.name)-1);
++ f.index = i->ifi_index;
+ f.mtu = mtu;
+- f.flags = 0;
++
+ fl = i->ifi_flags;
+ if (fl & IFF_UP)
+ f.flags |= IF_ADMIN_UP;
+diff --git sysdep/unix/krt.Y sysdep/unix/krt.Y
+index 8608196..18e1e52 100644
+--- sysdep/unix/krt.Y
++++ sysdep/unix/krt.Y
+@@ -31,7 +31,6 @@ kern_proto_start: proto_start KERNEL {
+ cf_error("Kernel protocol already defined");
+ #endif
+ cf_krt = this_proto = proto_config_new(&proto_unix_kernel, sizeof(struct krt_config), $1);
+- this_proto->preference = DEF_PREF_INHERITED;
+ THIS_KRT->scan_time = 60;
+ THIS_KRT->learn = THIS_KRT->persist = 0;
+ krt_scan_construct(THIS_KRT);
+@@ -67,7 +66,6 @@ kif_proto_start: proto_start DEVICE {
+ if (cf_kif)
+ cf_error("Kernel device protocol already defined");
+ cf_kif = this_proto = proto_config_new(&proto_unix_iface, sizeof(struct kif_config), $1);
+- this_proto->preference = DEF_PREF_DIRECT;
+ THIS_KIF->scan_time = 60;
+ init_list(&THIS_KIF->primary);
+ krt_if_construct(THIS_KIF);
+diff --git sysdep/unix/krt.c sysdep/unix/krt.c
+index e5a8ce1..231c5fc 100644
+--- sysdep/unix/krt.c
++++ sysdep/unix/krt.c
+@@ -104,6 +104,13 @@ kif_force_scan(void)
+ }
+ }
+
++void
++kif_request_scan(void)
++{
++ if (kif_proto && kif_scan_timer->expires > now)
++ tm_start(kif_scan_timer, 1);
++}
++
+ static struct proto *
+ kif_init(struct proto_config *c)
+ {
+@@ -236,6 +243,7 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
+ struct protocol proto_unix_iface = {
+ name: "Device",
+ template: "device%d",
++ preference: DEF_PREF_DIRECT,
+ preconfig: kif_preconfig,
+ init: kif_init,
+ start: kif_start,
+@@ -961,6 +969,7 @@ struct protocol proto_unix_kernel = {
+ name: "Kernel",
+ template: "kernel%d",
+ attr_class: EAP_KRT,
++ preference: DEF_PREF_INHERITED,
+ preconfig: krt_preconfig,
+ postconfig: krt_postconfig,
+ init: krt_init,
+diff --git sysdep/unix/krt.h sysdep/unix/krt.h
+index 7bb4fe7..b0c4dc5 100644
+--- sysdep/unix/krt.h
++++ sysdep/unix/krt.h
+@@ -77,6 +77,7 @@ extern pool *krt_pool;
+ if (pr->p.debug & fl) \
+ { log(L_TRACE "%s: " msg, pr->p.name , ## args); } } while(0)
+
++void kif_request_scan(void);
+ void krt_got_route(struct krt_proto *p, struct rte *e);
+ void krt_got_route_async(struct krt_proto *p, struct rte *e, int new);
+
diff --git a/net/bird/files/patch-protocoltemplates.diff b/net/bird/files/patch-protocoltemplates.diff
deleted file mode 100644
index d12a85f5fc19..000000000000
--- a/net/bird/files/patch-protocoltemplates.diff
+++ /dev/null
@@ -1,1085 +0,0 @@
-commit a7f23f581f5e3efe92ec97dfca7d01c66f31ab04
-Author: Ondrej Zajicek <santiago@crfreenet.org>
-Date: Mon Nov 7 00:31:23 2011 +0100
-
- Implements protocol templates.
-
- Based on the patch from Alexander V. Chernikov.
- Extended to support almost all protocols.
- Uses 'protocol bgp NAME from TEMPLATE { ... }' syntax.
-
-diff --git a/conf/cf-lex.l b/conf/cf-lex.l
-index 02ba4b3..ddfe8c7 100644
---- conf/cf-lex.l
-+++ conf/cf-lex.l
-@@ -533,6 +533,8 @@ cf_symbol_class_name(struct symbol *sym)
- return "routing table";
- case SYM_IPA:
- return "network address";
-+ case SYM_TEMPLATE:
-+ return "protocol template";
- default:
- return "unknown type";
- }
-diff --git a/conf/conf.c b/conf/conf.c
-index 5bdeece..4b605b3 100644
---- conf/conf.c
-+++ conf/conf.c
-@@ -377,3 +377,18 @@ cfg_strdup(char *c)
- memcpy(z, c, l);
- return z;
- }
-+
-+
-+void
-+cfg_copy_list(list *dest, list *src, unsigned node_size)
-+{
-+ node *dn, *sn;
-+
-+ init_list(dest);
-+ WALK_LIST(sn, *src)
-+ {
-+ dn = cfg_alloc(node_size);
-+ memcpy(dn, sn, node_size);
-+ add_tail(dest, dn);
-+ }
-+}
-diff --git a/conf/conf.h b/conf/conf.h
-index 142c6ad..8753baf 100644
---- conf/conf.h
-+++ conf/conf.h
-@@ -84,6 +84,7 @@ extern linpool *cfg_mem;
- #define cfg_allocu(size) lp_allocu(cfg_mem, size)
- #define cfg_allocz(size) lp_allocz(cfg_mem, size)
- char *cfg_strdup(char *c);
-+void cfg_copy_list(list *dest, list *src, unsigned node_size);
-
- /* Lexer */
-
-@@ -108,6 +109,7 @@ struct symbol {
- #define SYM_FILTER 4
- #define SYM_TABLE 5
- #define SYM_IPA 6
-+#define SYM_TEMPLATE 7
-
- #define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
-
-diff --git a/doc/bird.conf.example b/doc/bird.conf.example
-index 339898f..5e07ab5 100644
---- doc/bird.conf.example
-+++ doc/bird.conf.example
-@@ -202,3 +202,16 @@ protocol static {
- # reject;
- # };
- #}
-+#
-+# Template usage example
-+#template bgp rr_client {
-+# disabled;
-+# local as 65000;
-+# multihop;
-+# rr client;
-+# rr cluster id 1.0.0.1;
-+#}
-+#
-+#protocol bgp rr_abcd from rr_client {
-+# neighbor 10.1.4.7 as 65000;
-+#}
-diff --git a/doc/bird.sgml b/doc/bird.sgml
-index d454629..7f53f02 100644
---- doc/bird.sgml
-+++ doc/bird.sgml
-@@ -296,10 +296,21 @@ protocol rip {
- <tag>function <m/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag> Define a function. You can learn more
- about functions in the following chapter.
-
-- <tag>protocol rip|ospf|bgp|... <m/[name]/ { <m>protocol options</m> }</tag> Define a protocol
-- instance called <cf><m/name/</cf> (or with a name like "rip5" generated automatically if you don't specify any <cf><m/name/</cf>). You can learn more
-- about configuring protocols in their own chapters. You can run more than one instance of
-- most protocols (like RIP or BGP). By default, no instances are configured.
-+ <tag>protocol rip|ospf|bgp|... [<m/name/ [from <m/name2/]] { <m>protocol options</m> }</tag>
-+ Define a protocol instance called <cf><m/name/</cf> (or with a name like "rip5" generated
-+ automatically if you don't specify any <cf><m/name/</cf>). You can learn more about
-+ configuring protocols in their own chapters. When <cf>from <m/name2/</cf> expression is
-+ used, initial protocol options are taken from protocol or template <cf><m/name2/</cf>
-+ You can run more than one instance of most protocols (like RIP or BGP). By default, no
-+ instances are configured.
-+
-+ <tag>template rip|bgp|... [<m/name/ [from <m/name2/]] { <m>protocol options</m> }</tag>
-+ Define a protocol template instance called <cf><m/name/</cf> (or with a name like "bgp1"
-+ generated automatically if you don't specify any <cf><m/name/</cf>). Protocol templates can
-+ be used to group common options when many similarly configured protocol instances are to be
-+ defined. Protocol instances (and other templates) can use templates by using <cf/from/
-+ expression and the name of the template. At the moment templates (and <cf/from/ expression)
-+ are not implemented for OSPF protocol.
-
- <tag>define <m/constant/ = (<m/expression/)|<m/number/|<m/IP address/</tag>
- Define a constant. You can use it later in every place you could use a simple integer or an IP address.
-diff --git a/nest/config.Y b/nest/config.Y
-index dd4a9e0..a6baf4e 100644
---- nest/config.Y
-+++ nest/config.Y
-@@ -26,7 +26,7 @@ static int password_id;
- static inline void
- reset_passwords(void)
- {
-- this_p_list = NULL;
-+ this_p_list = NULL;
- }
-
- static inline list *
-@@ -37,10 +37,11 @@ get_passwords(void)
- return rv;
- }
-
-+#define DIRECT_CFG ((struct rt_dev_config *) this_proto)
-
- CF_DECLS
-
--CF_KEYWORDS(ROUTER, ID, PROTOCOL, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
-+CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
- CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
- CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
- CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE)
-@@ -58,7 +59,7 @@ CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULT
- %type <r> rtable
- %type <s> optsym
- %type <ra> r_args
--%type <i> echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport
-+%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport
- %type <ps> proto_patt proto_patt2
-
- CF_GRAMMAR
-@@ -115,20 +116,30 @@ newtab: TABLE SYM {
-
- CF_ADDTO(conf, proto)
-
--proto_start: PROTOCOL
-+proto_start:
-+ PROTOCOL { $$ = SYM_PROTO; }
-+ | TEMPLATE { $$ = SYM_TEMPLATE; }
- ;
-
- proto_name:
- /* EMPTY */ {
- struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
-- s->class = SYM_PROTO;
-+ s->class = this_proto->class;
- s->def = this_proto;
- this_proto->name = s->name;
- }
- | SYM {
-- cf_define_symbol($1, SYM_PROTO, this_proto);
-+ cf_define_symbol($1, this_proto->class, this_proto);
- this_proto->name = $1->name;
- }
-+ | SYM FROM SYM {
-+ if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
-+
-+ cf_define_symbol($1, this_proto->class, this_proto);
-+ this_proto->name = $1->name;
-+
-+ proto_copy_config(this_proto, $3->def);
-+ }
- ;
-
- proto_item:
-@@ -207,10 +218,9 @@ iface_patt_list:
- CF_ADDTO(proto, dev_proto '}')
-
- dev_proto_start: proto_start DIRECT {
-- struct rt_dev_config *p = proto_config_new(&proto_device, sizeof(struct rt_dev_config));
-- this_proto = &p->c;
-- p->c.preference = DEF_PREF_DIRECT;
-- init_list(&p->iface_list);
-+ this_proto = proto_config_new(&proto_device, sizeof(struct rt_dev_config), $1);
-+ this_proto->preference = DEF_PREF_DIRECT;
-+ init_list(&DIRECT_CFG->iface_list);
- }
- ;
-
-@@ -222,9 +232,8 @@ dev_proto:
-
- dev_iface_init:
- /* EMPTY */ {
-- struct rt_dev_config *p = (void *) this_proto;
- this_ipatt = cfg_allocz(sizeof(struct iface_patt));
-- add_tail(&p->iface_list, NODE this_ipatt);
-+ add_tail(&DIRECT_CFG->iface_list, NODE this_ipatt);
- init_list(&this_ipatt->ipn_list);
- }
- ;
-diff --git a/nest/proto.c b/nest/proto.c
-index 4a154d5..d55c348 100644
---- nest/proto.c
-+++ nest/proto.c
-@@ -175,6 +175,7 @@ proto_flush_hooks(struct proto *p)
- * proto_config_new - create a new protocol configuration
- * @pr: protocol the configuration will belong to
- * @size: size of the structure including generic data
-+ * @class: SYM_PROTO or SYM_TEMPLATE
- *
- * Whenever the configuration file says that a new instance
- * of a routing protocol should be created, the parser calls
-@@ -183,16 +184,23 @@ proto_flush_hooks(struct proto *p)
- * containing all the generic items followed by protocol-specific
- * ones). Also, the configuration entry gets added to the list
- * of protocol instances kept in the configuration.
-+ *
-+ * The function is also used to create protocol templates (when class
-+ * SYM_TEMPLATE is specified), the only difference is that templates
-+ * are not added to the list of protocol instances and therefore not
-+ * initialized during protos_commit()).
- */
- void *
--proto_config_new(struct protocol *pr, unsigned size)
-+proto_config_new(struct protocol *pr, unsigned size, int class)
- {
- struct proto_config *c = cfg_allocz(size);
-
-- add_tail(&new_config->protos, &c->n);
-+ if (class == SYM_PROTO)
-+ add_tail(&new_config->protos, &c->n);
- c->global = new_config;
- c->protocol = pr;
- c->name = pr->name;
-+ c->class = class;
- c->out_filter = FILTER_REJECT;
- c->table = c->global->master_rtc;
- c->debug = new_config->proto_default_debug;
-@@ -201,6 +209,50 @@ proto_config_new(struct protocol *pr, unsigned size)
- }
-
- /**
-+ * proto_copy_config - copy a protocol configuration
-+ * @dest: destination protocol configuration
-+ * @src: source protocol configuration
-+ *
-+ * Whenever a new instance of a routing protocol is created from the
-+ * template, proto_copy_config() is called to copy a content of
-+ * the source protocol configuration to the new protocol configuration.
-+ * Name, class and a node in protos list of @dest are kept intact.
-+ * copy_config() protocol hook is used to copy protocol-specific data.
-+ */
-+void
-+proto_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ node old_node;
-+ int old_class;
-+ char *old_name;
-+
-+ if (dest->protocol != src->protocol)
-+ cf_error("Can't copy configuration from a different protocol type");
-+
-+ if (dest->protocol->copy_config == NULL)
-+ cf_error("Inheriting configuration for %s is not supported", src->protocol->name);
-+
-+ DBG("Copying configuration from %s to %s\n", src->name, dest->name);
-+
-+ /*
-+ * Copy struct proto_config here. Keep original node, class and name.
-+ * protocol-specific config copy is handled by protocol copy_config() hook
-+ */
-+
-+ old_node = dest->n;
-+ old_class = dest->class;
-+ old_name = dest->name;
-+
-+ memcpy(dest, src, sizeof(struct proto_config));
-+
-+ dest->n = old_node;
-+ dest->class = old_class;
-+ dest->name = old_name;
-+
-+ dest->protocol->copy_config(dest, src);
-+}
-+
-+/**
- * protos_preconfig - pre-configuration processing
- * @c: new configuration
- *
-@@ -230,7 +282,8 @@ protos_preconfig(struct config *c)
- * @c: new configuration
- *
- * This function calls the postconfig() hooks of all protocol
-- * instances specified in configuration @c.
-+ * instances specified in configuration @c. The hooks are not
-+ * called for protocol templates.
- */
- void
- protos_postconfig(struct config *c)
-@@ -366,14 +419,15 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
- {
- struct proto_config *oc, *nc;
- struct proto *p, *n;
-+ struct symbol *sym;
-
- DBG("protos_commit:\n");
- if (old)
- {
- WALK_LIST(oc, old->protos)
- {
-- struct proto *p = oc->proto;
-- struct symbol *sym = cf_find_symbol(oc->name);
-+ p = oc->proto;
-+ sym = cf_find_symbol(oc->name);
- if (sym && sym->class == SYM_PROTO && !new->shutdown)
- {
- /* Found match, let's check if we can smoothly switch to new configuration */
-diff --git a/nest/protocol.h b/nest/protocol.h
-index f95905a..a7518c2 100644
---- nest/protocol.h
-+++ nest/protocol.h
-@@ -53,6 +53,7 @@ struct protocol {
- void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs); /* Get route information (for `show route' command) */
- int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
- void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
-+ void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
- };
-
- void protos_build(void);
-@@ -85,12 +86,15 @@ struct proto_config {
- struct proto *proto; /* Instance we've created */
- char *name;
- char *dsc;
-+ int class; /* SYM_PROTO or SYM_TEMPLATE */
- u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
- unsigned preference, disabled; /* Generic parameters */
- u32 router_id; /* Protocol specific router ID */
- struct rtable_config *table; /* Table we're attached to */
- struct filter *in_filter, *out_filter; /* Attached filters */
-
-+ /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
-+
- /* Protocol-specific data follow... */
- };
-
-@@ -203,9 +207,14 @@ struct proto_spec {
-
-
- void *proto_new(struct proto_config *, unsigned size);
--void *proto_config_new(struct protocol *, unsigned size);
-+void *proto_config_new(struct protocol *, unsigned size, int class);
-+void proto_copy_config(struct proto_config *dest, struct proto_config *src);
- void proto_request_feeding(struct proto *p);
-
-+static inline void
-+proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
-+{ memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
-+
- void proto_cmd_show(struct proto *, unsigned int, int);
- void proto_cmd_disable(struct proto *, unsigned int, int);
- void proto_cmd_enable(struct proto *, unsigned int, int);
-diff --git a/nest/rt-dev.c b/nest/rt-dev.c
-index 239bd26..497ee80 100644
---- nest/rt-dev.c
-+++ nest/rt-dev.c
-@@ -92,9 +92,24 @@ dev_reconfigure(struct proto *p, struct proto_config *new)
- return iface_patts_equal(&o->iface_list, &n->iface_list, NULL);
- }
-
-+static void
-+dev_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ struct rt_dev_config *d = (struct rt_dev_config *) dest;
-+ struct rt_dev_config *s = (struct rt_dev_config *) src;
-+
-+ /*
-+ * We copy iface_list as ifaces can be shared by more direct protocols.
-+ * Copy suffices to be is shallow, because new nodes can be added, but
-+ * old nodes cannot be modified (although they contain internal lists).
-+ */
-+ cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct iface_patt));
-+}
-+
- struct protocol proto_device = {
- name: "Direct",
- template: "direct%d",
- init: dev_init,
-- reconfigure: dev_reconfigure
-+ reconfigure: dev_reconfigure,
-+ copy_config: dev_copy_config
- };
-diff --git a/nest/rt-dev.h b/nest/rt-dev.h
-index 64f2cd9..c36d074 100644
---- nest/rt-dev.h
-+++ nest/rt-dev.h
-@@ -11,7 +11,7 @@
-
- struct rt_dev_config {
- struct proto_config c;
-- list iface_list;
-+ list iface_list; /* list of struct iface_patt */
- };
-
- #endif
-diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
-index 4e4ca9f..675342d 100644
---- proto/bgp/bgp.c
-+++ proto/bgp/bgp.c
-@@ -919,6 +919,73 @@ bgp_init(struct proto_config *C)
- return P;
- }
-
-+
-+void
-+bgp_check_config(struct bgp_config *c)
-+{
-+ int internal = (c->local_as == c->remote_as);
-+
-+ /* Do not check templates at all */
-+ if (c->c.class == SYM_TEMPLATE)
-+ return;
-+
-+ if (!c->local_as)
-+ cf_error("Local AS number must be set");
-+
-+ if (!c->remote_as)
-+ cf_error("Neighbor must be configured");
-+
-+ if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF))
-+ cf_error("Neighbor AS number out of range (AS4 not available)");
-+
-+ if (!internal && c->rr_client)
-+ cf_error("Only internal neighbor can be RR client");
-+
-+ if (internal && c->rs_client)
-+ cf_error("Only external neighbor can be RS client");
-+
-+ if (c->multihop && (c->gw_mode == GW_DIRECT))
-+ cf_error("Multihop BGP cannot use direct gateway mode");
-+
-+ /* Different default based on rs_client */
-+ if (!c->missing_lladdr)
-+ c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
-+
-+ /* Different default for gw_mode */
-+ if (!c->gw_mode)
-+ c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
-+}
-+
-+static int
-+bgp_reconfigure(struct proto *P, struct proto_config *C)
-+{
-+ struct bgp_config *new = (struct bgp_config *) C;
-+ struct bgp_proto *p = (struct bgp_proto *) P;
-+ struct bgp_config *old = p->cf;
-+
-+ int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
-+ ((byte *) new) + sizeof(struct proto_config),
-+ // password item is last and must be checked separately
-+ OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
-+ && ((!old->password && !new->password)
-+ || (old->password && new->password && !strcmp(old->password, new->password)))
-+ && (get_igp_table(old) == get_igp_table(new));
-+
-+ /* We should update our copy of configuration ptr as old configuration will be freed */
-+ if (same)
-+ p->cf = new;
-+
-+ return same;
-+}
-+
-+static void
-+bgp_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ /* Just a shallow copy */
-+ proto_copy_rest(dest, src, sizeof(struct bgp_config));
-+}
-+
-+
- /**
- * bgp_error - report a protocol error
- * @c: connection
-@@ -983,38 +1050,6 @@ bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code)
- p->last_error_code = code;
- }
-
--void
--bgp_check(struct bgp_config *c)
--{
-- int internal = (c->local_as == c->remote_as);
--
-- if (!c->local_as)
-- cf_error("Local AS number must be set");
--
-- if (!c->remote_as)
-- cf_error("Neighbor must be configured");
--
-- if (!(c->capabilities && c->enable_as4) && (c->remote_as > 0xFFFF))
-- cf_error("Neighbor AS number out of range (AS4 not available)");
--
-- if (!internal && c->rr_client)
-- cf_error("Only internal neighbor can be RR client");
--
-- if (internal && c->rs_client)
-- cf_error("Only external neighbor can be RS client");
--
-- if (c->multihop && (c->gw_mode == GW_DIRECT))
-- cf_error("Multihop BGP cannot use direct gateway mode");
--
-- /* Different default based on rs_client */
-- if (!c->missing_lladdr)
-- c->missing_lladdr = c->rs_client ? MLL_IGNORE : MLL_SELF;
--
-- /* Different default for gw_mode */
-- if (!c->gw_mode)
-- c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
--}
--
- static char *bgp_state_names[] = { "Idle", "Connect", "Active", "OpenSent", "OpenConfirm", "Established", "Close" };
- static char *bgp_err_classes[] = { "", "Error: ", "Socket: ", "Received: ", "BGP Error: ", "Automatic shutdown: ", ""};
- static char *bgp_misc_errors[] = { "", "Neighbor lost", "Invalid next hop", "Kernel MD5 auth failed", "No listening socket" };
-@@ -1124,28 +1159,6 @@ bgp_show_proto_info(struct proto *P)
- }
- }
-
--static int
--bgp_reconfigure(struct proto *P, struct proto_config *C)
--{
-- struct bgp_config *new = (struct bgp_config *) C;
-- struct bgp_proto *p = (struct bgp_proto *) P;
-- struct bgp_config *old = p->cf;
--
-- int same = !memcmp(((byte *) old) + sizeof(struct proto_config),
-- ((byte *) new) + sizeof(struct proto_config),
-- // password item is last and must be checked separately
-- OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
-- && ((!old->password && !new->password)
-- || (old->password && new->password && !strcmp(old->password, new->password)))
-- && (get_igp_table(old) == get_igp_table(new));
--
-- /* We should update our copy of configuration ptr as old configuration will be freed */
-- if (same)
-- p->cf = new;
--
-- return same;
--}
--
- struct protocol proto_bgp = {
- name: "BGP",
- template: "bgp%d",
-@@ -1155,6 +1168,7 @@ struct protocol proto_bgp = {
- shutdown: bgp_shutdown,
- cleanup: bgp_cleanup,
- reconfigure: bgp_reconfigure,
-+ copy_config: bgp_copy_config,
- get_status: bgp_get_status,
- get_attr: bgp_get_attr,
- get_route_info: bgp_get_route_info,
-diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
-index 16e8ea8..437ba33 100644
---- proto/bgp/bgp.h
-+++ proto/bgp/bgp.h
-@@ -141,7 +141,7 @@ extern struct linpool *bgp_linpool;
-
-
- void bgp_start_timer(struct timer *t, int value);
--void bgp_check(struct bgp_config *c);
-+void bgp_check_config(struct bgp_config *c);
- void bgp_error(struct bgp_conn *c, unsigned code, unsigned subcode, byte *data, int len);
- void bgp_close_conn(struct bgp_conn *c);
- void bgp_update_startup_delay(struct bgp_proto *p);
-diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
-index 19d757a..03c233d 100644
---- proto/bgp/config.Y
-+++ proto/bgp/config.Y
-@@ -29,10 +29,10 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY,
-
- CF_GRAMMAR
-
--CF_ADDTO(proto, bgp_proto '}' { bgp_check(BGP_CFG); } )
-+CF_ADDTO(proto, bgp_proto '}' { bgp_check_config(BGP_CFG); } )
-
- bgp_proto_start: proto_start BGP {
-- this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config));
-+ this_proto = proto_config_new(&proto_bgp, sizeof(struct bgp_config), $1);
- this_proto->preference = DEF_PREF_BGP;
- BGP_CFG->hold_time = 240;
- BGP_CFG->connect_retry_time = 120;
-diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
-index ec7da8e..4ada41e 100644
---- proto/ospf/config.Y
-+++ proto/ospf/config.Y
-@@ -128,7 +128,7 @@ CF_GRAMMAR
- CF_ADDTO(proto, ospf_proto '}' { ospf_proto_finish(); } )
-
- ospf_proto_start: proto_start OSPF {
-- this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config));
-+ this_proto = proto_config_new(&proto_ospf, sizeof(struct ospf_config), $1);
- this_proto->preference = DEF_PREF_OSPF;
- init_list(&OSPF_CFG->area_list);
- init_list(&OSPF_CFG->vlink_list);
-diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
-index 2e99b0f..60a34fb 100644
---- proto/ospf/ospf.h
-+++ proto/ospf/ospf.h
-@@ -85,8 +85,8 @@ struct ospf_config
- byte rfc1583;
- byte abr;
- int ecmp;
-- list area_list;
-- list vlink_list;
-+ list area_list; /* list of struct ospf_area_config */
-+ list vlink_list; /* list of struct ospf_iface_patt */
- };
-
- struct nbma_node
-diff --git a/proto/pipe/config.Y b/proto/pipe/config.Y
-index e1c981b..4478afe 100644
---- proto/pipe/config.Y
-+++ proto/pipe/config.Y
-@@ -23,7 +23,7 @@ CF_GRAMMAR
- CF_ADDTO(proto, pipe_proto '}')
-
- pipe_proto_start: proto_start PIPE {
-- this_proto = proto_config_new(&proto_pipe, sizeof(struct pipe_config));
-+ this_proto = proto_config_new(&proto_pipe, sizeof(struct pipe_config), $1);
- this_proto->preference = DEF_PREF_PIPE;
- PIPE_CFG->mode = PIPE_TRANSPARENT;
- }
-diff --git a/proto/pipe/pipe.c b/proto/pipe/pipe.c
-index e557097..420c5a9 100644
---- proto/pipe/pipe.c
-+++ proto/pipe/pipe.c
-@@ -165,14 +165,6 @@ pipe_postconfig(struct proto_config *C)
- cf_error("Primary table and peer table must be different");
- }
-
--static void
--pipe_get_status(struct proto *P, byte *buf)
--{
-- struct pipe_proto *p = (struct pipe_proto *) P;
--
-- bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name);
--}
--
- static int
- pipe_reconfigure(struct proto *P, struct proto_config *new)
- {
-@@ -186,6 +178,21 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
- return 1;
- }
-
-+static void
-+pipe_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ /* Just a shallow copy, not many items here */
-+ proto_copy_rest(dest, src, sizeof(struct pipe_config));
-+}
-+
-+static void
-+pipe_get_status(struct proto *P, byte *buf)
-+{
-+ struct pipe_proto *p = (struct pipe_proto *) P;
-+
-+ bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name);
-+}
-+
-
- struct protocol proto_pipe = {
- name: "Pipe",
-@@ -195,5 +202,6 @@ struct protocol proto_pipe = {
- start: pipe_start,
- cleanup: pipe_cleanup,
- reconfigure: pipe_reconfigure,
-+ copy_config: pipe_copy_config,
- get_status: pipe_get_status,
- };
-diff --git a/proto/radv/radv.c b/proto/radv/radv.c
-index 01cb689..42d4bff 100644
---- proto/radv/radv.c
-+++ proto/radv/radv.c
-@@ -318,6 +318,19 @@ radv_reconfigure(struct proto *p, struct proto_config *c)
- return 1;
- }
-
-+static void
-+radv_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ struct radv_config *d = (struct radv_config *) dest;
-+ struct radv_config *s = (struct radv_config *) src;
-+
-+ /* We clean up patt_list, ifaces are non-sharable */
-+ init_list(&d->patt_list);
-+
-+ /* We copy pref_list, shallow copy suffices */
-+ cfg_copy_list(&d->iface_list, &s->iface_list, sizeof(struct iface_patt));
-+}
-+
-
- struct protocol proto_radv = {
- .name = "RAdv",
-@@ -325,5 +338,6 @@ struct protocol proto_radv = {
- .init = radv_init,
- .start = radv_start,
- .shutdown = radv_shutdown,
-- .reconfigure = radv_reconfigure
-+ .reconfigure = radv_reconfigure,
-+ .copy_config = radv_copy_config
- };
-diff --git a/proto/radv/radv.h b/proto/radv/radv.h
-index fe121f2..12bfe42 100644
---- proto/radv/radv.h
-+++ proto/radv/radv.h
-@@ -46,14 +46,14 @@
- struct radv_config
- {
- struct proto_config c;
-- list patt_list; /* List of iface configs */
-- list pref_list; /* Global list of prefix configs */
-+ list patt_list; /* List of iface configs (struct radv_iface_config) */
-+ list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */
- };
-
- struct radv_iface_config
- {
- struct iface_patt i;
-- list pref_list; /* Local list of prefix configs */
-+ list pref_list; /* Local list of prefix configs (struct radv_prefix_config) */
-
- u32 min_ra_int; /* Standard options from RFC 4261 */
- u32 max_ra_int;
-@@ -64,7 +64,7 @@ struct radv_iface_config
- u32 link_mtu;
- u32 reachable_time;
- u32 retrans_timer;
-- u32 current_hop_limit;
-+ u32 current_hop_limit;
- u32 default_lifetime;
- };
-
-diff --git a/proto/rip/config.Y b/proto/rip/config.Y
-index 2df0c5c..cd4f30e 100644
---- proto/rip/config.Y
-+++ proto/rip/config.Y
-@@ -37,7 +37,7 @@ CF_GRAMMAR
- CF_ADDTO(proto, rip_cfg '}' { RIP_CFG->passwords = get_passwords(); } )
-
- rip_cfg_start: proto_start RIP {
-- this_proto = proto_config_new(&proto_rip, sizeof(struct rip_proto_config));
-+ this_proto = proto_config_new(&proto_rip, sizeof(struct rip_proto_config), $1);
- rip_init_config(RIP_CFG);
- }
- ;
-diff --git a/proto/rip/rip.c b/proto/rip/rip.c
-index 1266380..543aa30 100644
---- proto/rip/rip.c
-+++ proto/rip/rip.c
-@@ -1015,6 +1015,19 @@ rip_reconfigure(struct proto *p, struct proto_config *c)
- sizeof(struct rip_proto_config) - generic);
- }
-
-+static void
-+rip_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ /* Shallow copy of everything */
-+ proto_copy_rest(dest, src, sizeof(struct rip_proto_config));
-+
-+ /* We clean up iface_list, ifaces are non-sharable */
-+ init_list(&((struct rip_proto_config *) dest)->iface_list);
-+
-+ /* Copy of passwords is OK, it just will be replaced in dest when used */
-+}
-+
-+
- struct protocol proto_rip = {
- name: "RIP",
- template: "rip%d",
-@@ -1026,4 +1039,5 @@ struct protocol proto_rip = {
- dump: rip_dump,
- start: rip_start,
- reconfigure: rip_reconfigure,
-+ copy_config: rip_copy_config
- };
-diff --git a/proto/static/config.Y b/proto/static/config.Y
-index 77d2419..621fdf9 100644
---- proto/static/config.Y
-+++ proto/static/config.Y
-@@ -26,7 +26,7 @@ CF_GRAMMAR
- CF_ADDTO(proto, static_proto '}')
-
- static_proto_start: proto_start STATIC {
-- this_proto = proto_config_new(&proto_static, sizeof(struct static_config));
-+ this_proto = proto_config_new(&proto_static, sizeof(struct static_config), $1);
- static_init_config((struct static_config *) this_proto);
- }
- ;
-diff --git a/proto/static/static.c b/proto/static/static.c
-index 2f33d81..e5b293c 100644
---- proto/static/static.c
-+++ proto/static/static.c
-@@ -470,6 +470,58 @@ static_reconfigure(struct proto *p, struct proto_config *new)
- return 1;
- }
-
-+static void
-+static_copy_routes(list *dlst, list *slst)
-+{
-+ struct static_route *dr, *sr;
-+
-+ init_list(dlst);
-+ WALK_LIST(sr, *slst)
-+ {
-+ /* copy one route */
-+ dr = cfg_alloc(sizeof(struct static_route));
-+ memcpy(dr, sr, sizeof(struct static_route));
-+
-+ /* This fn is supposed to be called on fresh src routes, which have 'live'
-+ fields (like .chain, .neigh or .installed) zero, so no need to zero them */
-+
-+ /* We need to copy multipath chain, because there are backptrs in 'if_name' */
-+ if (dr->dest == RTD_MULTIPATH)
-+ {
-+ struct static_route *md, *ms, **mp_last;
-+
-+ mp_last = &(dr->mp_next);
-+ for (ms = sr->mp_next; ms; ms = ms->mp_next)
-+ {
-+ md = cfg_alloc(sizeof(struct static_route));
-+ memcpy(md, ms, sizeof(struct static_route));
-+ md->if_name = (void *) dr; /* really */
-+
-+ *mp_last = md;
-+ mp_last = &(md->mp_next);
-+ }
-+ *mp_last = NULL;
-+ }
-+
-+ add_tail(dlst, (node *) dr);
-+ }
-+}
-+
-+static void
-+static_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ struct static_config *d = (struct static_config *) dest;
-+ struct static_config *s = (struct static_config *) src;
-+
-+ /* Shallow copy of everything */
-+ proto_copy_rest(dest, src, sizeof(struct static_config));
-+
-+ /* Copy route lists */
-+ static_copy_routes(&d->iface_routes, &s->iface_routes);
-+ static_copy_routes(&d->other_routes, &s->other_routes);
-+}
-+
-+
- struct protocol proto_static = {
- name: "Static",
- template: "static%d",
-@@ -479,6 +531,7 @@ struct protocol proto_static = {
- shutdown: static_shutdown,
- cleanup: static_cleanup,
- reconfigure: static_reconfigure,
-+ copy_config: static_copy_config
- };
-
- static void
-diff --git a/sysdep/bsd/krt-iface.h b/sysdep/bsd/krt-iface.h
-index 7d96fe8..7f0d52b 100644
---- sysdep/bsd/krt-iface.h
-+++ sysdep/bsd/krt-iface.h
-@@ -12,6 +12,14 @@
- /*
- * We don't have split iface/scan/set parts. See krt-sock.h.
- */
-+
-+struct krt_if_params {
-+};
-+
-+struct krt_if_status {
-+};
-+
- static inline int kif_params_same(struct krt_if_params *old UNUSED, struct krt_if_params *new UNUSED) { return 1; }
-+static inline void kif_copy_params(struct krt_if_params *dest UNUSED, struct krt_if_params *src UNUSED) { }
-
- #endif
-diff --git a/sysdep/bsd/krt-scan.h b/sysdep/bsd/krt-scan.h
-index 284df5e..19cd930 100644
---- sysdep/bsd/krt-scan.h
-+++ sysdep/bsd/krt-scan.h
-@@ -17,5 +17,6 @@ struct krt_scan_status {
- };
-
- static inline int krt_scan_params_same(struct krt_scan_params *o UNUSED, struct krt_scan_params *n UNUSED) { return 1; }
-+static inline void krt_scan_copy_params(struct krt_scan_params *d UNUSED, struct krt_scan_params *s UNUSED) { }
-
- #endif
-diff --git a/sysdep/bsd/krt-sock.h b/sysdep/bsd/krt-sock.h
-index d2a7efb..aab639c 100644
---- sysdep/bsd/krt-sock.h
-+++ sysdep/bsd/krt-sock.h
-@@ -34,13 +34,9 @@ struct krt_set_params {
- struct krt_set_status {
- };
-
--struct krt_if_params {
--};
--
--struct krt_if_status {
--};
--
- static inline int krt_set_params_same(struct krt_set_params *o UNUSED, struct krt_set_params *n UNUSED) { return 1; }
-+static inline void krt_set_copy_params(struct krt_set_params *d UNUSED, struct krt_set_params *s UNUSED) { }
-+
- void krt_read_msg(struct proto *p, struct ks_msg *msg, int scan);
-
- #endif
-diff --git a/sysdep/linux/netlink/krt-iface.h b/sysdep/linux/netlink/krt-iface.h
-index f44ca27..770c6e2 100644
---- sysdep/linux/netlink/krt-iface.h
-+++ sysdep/linux/netlink/krt-iface.h
-@@ -24,5 +24,6 @@ static inline void krt_if_shutdown(struct kif_proto *p UNUSED) { };
- static inline void krt_if_io_init(void) { };
-
- static inline int kif_params_same(struct krt_if_params *old UNUSED, struct krt_if_params *new UNUSED) { return 1; }
-+static inline void kif_copy_params(struct krt_if_params *dest UNUSED, struct krt_if_params *src UNUSED) { }
-
- #endif
-diff --git a/sysdep/linux/netlink/krt-scan.h b/sysdep/linux/netlink/krt-scan.h
-index 7885f07..9b5e075 100644
---- sysdep/linux/netlink/krt-scan.h
-+++ sysdep/linux/netlink/krt-scan.h
-@@ -30,4 +30,7 @@ static inline int krt_scan_params_same(struct krt_scan_params *o, struct krt_sca
- return o->table_id == n->table_id;
- }
-
-+static inline void krt_scan_copy_params(struct krt_scan_params *d UNUSED, struct krt_scan_params *s UNUSED) { }
-+/* table_id copied in krt_copy_config() */
-+
- #endif
-diff --git a/sysdep/linux/netlink/krt-set.h b/sysdep/linux/netlink/krt-set.h
-index 83d082d..4a08217 100644
---- sysdep/linux/netlink/krt-set.h
-+++ sysdep/linux/netlink/krt-set.h
-@@ -23,5 +23,6 @@ static inline void krt_set_construct(struct krt_config *c UNUSED) { };
- static inline void krt_set_start(struct krt_proto *p UNUSED, int first UNUSED) { };
- static inline void krt_set_shutdown(struct krt_proto *p UNUSED, int last UNUSED) { };
- static inline int krt_set_params_same(struct krt_set_params *o UNUSED, struct krt_set_params *n UNUSED) { return 1; }
-+static inline void krt_set_copy_params(struct krt_set_params *d UNUSED, struct krt_set_params *s UNUSED) { }
-
- #endif
-diff --git a/sysdep/unix/krt-iface.h b/sysdep/unix/krt-iface.h
-index 48075d6..9e12bcc 100644
---- sysdep/unix/krt-iface.h
-+++ sysdep/unix/krt-iface.h
-@@ -17,6 +17,7 @@ struct krt_if_status {
-
- extern int if_scan_sock;
-
--static inline int kif_params_same(struct krt_if_params *old, struct krt_if_params *new) { return 1; }
-+static inline int kif_params_same(struct krt_if_params *old UNUSED, struct krt_if_params *new UNUSED) { return 1; }
-+static inline void kif_copy_params(struct krt_if_params *dest UNUSED, struct krt_if_params *src UNUSED) { }
-
- #endif
-diff --git a/sysdep/unix/krt-set.h b/sysdep/unix/krt-set.h
-index 5d0b213..87cffcf 100644
---- sysdep/unix/krt-set.h
-+++ sysdep/unix/krt-set.h
-@@ -16,5 +16,6 @@ struct krt_set_status {
- };
-
- static inline int krt_set_params_same(struct krt_set_params *o UNUSED, struct krt_set_params *n UNUSED) { return 1; }
-+static inline void krt_set_copy_params(struct krt_set_params *d UNUSED, struct krt_set_params *s UNUSED) { }
-
- #endif
-diff --git a/sysdep/unix/krt.Y b/sysdep/unix/krt.Y
-index 0375a13..8608196 100644
---- sysdep/unix/krt.Y
-+++ sysdep/unix/krt.Y
-@@ -30,7 +30,7 @@ kern_proto_start: proto_start KERNEL {
- if (cf_krt)
- cf_error("Kernel protocol already defined");
- #endif
-- cf_krt = this_proto = proto_config_new(&proto_unix_kernel, sizeof(struct krt_config));
-+ cf_krt = this_proto = proto_config_new(&proto_unix_kernel, sizeof(struct krt_config), $1);
- this_proto->preference = DEF_PREF_INHERITED;
- THIS_KRT->scan_time = 60;
- THIS_KRT->learn = THIS_KRT->persist = 0;
-@@ -66,7 +66,7 @@ CF_ADDTO(proto, kif_proto '}')
- kif_proto_start: proto_start DEVICE {
- if (cf_kif)
- cf_error("Kernel device protocol already defined");
-- cf_kif = this_proto = proto_config_new(&proto_unix_iface, sizeof(struct kif_config));
-+ cf_kif = this_proto = proto_config_new(&proto_unix_iface, sizeof(struct kif_config), $1);
- this_proto->preference = DEF_PREF_DIRECT;
- THIS_KIF->scan_time = 60;
- init_list(&THIS_KIF->primary);
-diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
-index 7057070..e5a8ce1 100644
---- sysdep/unix/krt.c
-+++ sysdep/unix/krt.c
-@@ -216,6 +216,23 @@ kif_reconfigure(struct proto *p, struct proto_config *new)
- return 1;
- }
-
-+static void
-+kif_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ struct kif_config *d = (struct kif_config *) dest;
-+ struct kif_config *s = (struct kif_config *) src;
-+
-+ /* Shallow copy of everything (just scan_time currently) */
-+ proto_copy_rest(dest, src, sizeof(struct krt_config));
-+
-+ /* Copy primary addr list */
-+ cfg_copy_list(&d->primary, &s->primary, sizeof(struct kif_primary_item));
-+
-+ /* Fix sysdep parts */
-+ kif_copy_params(&d->iface, &s->iface);
-+}
-+
-+
- struct protocol proto_unix_iface = {
- name: "Device",
- template: "device%d",
-@@ -224,6 +241,7 @@ struct protocol proto_unix_iface = {
- start: kif_start,
- shutdown: kif_shutdown,
- reconfigure: kif_reconfigure,
-+ copy_config: kif_copy_config
- };
-
- /*
-@@ -908,6 +926,19 @@ krt_reconfigure(struct proto *p, struct proto_config *new)
- ;
- }
-
-+static void
-+krt_copy_config(struct proto_config *dest, struct proto_config *src)
-+{
-+ struct krt_config *d = (struct krt_config *) dest;
-+ struct krt_config *s = (struct krt_config *) src;
-+
-+ /* Shallow copy of everything */
-+ proto_copy_rest(dest, src, sizeof(struct krt_config));
-+
-+ /* Fix sysdep parts */
-+ krt_set_copy_params(&d->set, &s->set);
-+ krt_scan_copy_params(&d->scan, &s->scan);
-+}
-
- static int
- krt_get_attr(eattr * a, byte * buf, int buflen UNUSED)
-@@ -936,6 +967,7 @@ struct protocol proto_unix_kernel = {
- start: krt_start,
- shutdown: krt_shutdown,
- reconfigure: krt_reconfigure,
-+ copy_config: krt_copy_config,
- get_attr: krt_get_attr,
- #ifdef KRT_ALLOW_LEARN
- dump: krt_dump,
-diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h
-index f83e6ee..7bb4fe7 100644
---- sysdep/unix/krt.h
-+++ sysdep/unix/krt.h
-@@ -100,7 +100,7 @@ struct kif_config {
- struct proto_config c;
- struct krt_if_params iface;
- int scan_time; /* How often we re-scan interfaces */
-- list primary; /* Preferences for primary addresses */
-+ list primary; /* Preferences for primary addresses (struct kif_primary_item) */
- };
-
- struct kif_proto {