Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save paigeadelethompson/e295b2d36f7e536595d6c32c760b2a1f to your computer and use it in GitHub Desktop.

Select an option

Save paigeadelethompson/e295b2d36f7e536595d6c32c760b2a1f to your computer and use it in GitHub Desktop.
diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile
index ce778ad4848..f6145755917 100644
--- a/sbin/ifconfig/Makefile
+++ b/sbin/ifconfig/Makefile
@@ -35,7 +35,7 @@ SRCS+= ifgif.c # GIF reversed header workaround
SRCS+= ifipsec.c # IPsec VTI
SRCS+= sfp.c # SFP/SFP+ information
-LIBADD+= ifconfig m util xo
+LIBADD+= ifconfig m util xo bsdxml
CFLAGS+= -I${SRCTOP}/lib/libifconfig -I${OBJTOP}/lib/libifconfig
.if ${MK_WIRELESS_SUPPORT} != "no"
@@ -51,6 +51,7 @@ SRCS+= ifpfsync.c # pfsync(4) support
SRCS+= ifbridge.c # bridge support
SRCS+= iflagg.c # lagg support
+SRCS+= ifconfig_xml.c # XML interface configuration
.if ${MK_EXPERIMENTAL} != "no"
CFLAGS+= -DDRAFT_IETF_6MAN_IPV6ONLY_FLAG
diff --git a/sbin/ifconfig/ifconfig_xml.c b/sbin/ifconfig/ifconfig_xml.c
new file mode 100644
index 00000000000..047c40afa16
--- /dev/null
+++ b/sbin/ifconfig/ifconfig_xml.c
@@ -0,0 +1,890 @@
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <bsdxml.h>
+#include "ifconfig.h"
+#include "ifconfig_xml.h"
+
+#define MAX_INTERFACES 1024
+#define MAX_ARGS 256
+
+int ifconfig_xml_dry_run;
+
+enum {
+ ELEM_NONE,
+ ELEM_IFCONFIG,
+ ELEM_INTERFACE,
+ ELEM_IFNAME,
+ ELEM_IF_FLAGS,
+ ELEM_METRIC,
+ ELEM_MTU,
+ ELEM_DESCRIPTION,
+ ELEM_DESCR,
+ ELEM_LINK_ADDR_PARENT,
+ ELEM_ADDR_TYPE,
+ ELEM_LINK_ADDR_VAL,
+ ELEM_GROUP,
+ ELEM_GROUP_NAME,
+ ELEM_MEDIA,
+ ELEM_MEDIA_TYPE,
+ ELEM_MEDIA_SUBTYPE,
+ ELEM_MEDIA_OPTION,
+ ELEM_IF_CAP,
+ ELEM_OPTIONS,
+ ELEM_OPTIONS_HEX,
+ ELEM_STATUS,
+ ELEM_FIB,
+ ELEM_TUNNEL_FIB,
+ ELEM_ADDRESS_CONTAINER,
+ ELEM_INET_ADDR,
+ ELEM_INET6_ADDR,
+ ELEM_DST_ADDR,
+ ELEM_NETMASK,
+ ELEM_BROADCAST,
+ ELEM_PREFIXLEN,
+ ELEM_BRIDGE,
+ ELEM_BRIDGE_PRIORITY,
+ ELEM_HELLOTIME,
+ ELEM_FWDDELAY,
+ ELEM_MAXAGE,
+ ELEM_HOLDCNT,
+ ELEM_STP_PROTO,
+ ELEM_MAXADDR,
+ ELEM_TIMEOUT,
+ ELEM_MEMBER,
+ ELEM_MEMBER_NAME,
+ ELEM_PORT_PRIORITY,
+ ELEM_PATH_COST,
+ ELEM_VLAN_PROTO,
+ ELEM_LAGG_PROTO,
+ ELEM_LAGG_HASH_L2,
+ ELEM_LAGG_HASH_L3,
+ ELEM_LAGG_HASH_L4,
+ ELEM_LAGG_PORT,
+ ELEM_LAGG_PORT_NAME,
+ ELEM_TUNNEL_SRC,
+ ELEM_TUNNEL_DST,
+ ELEM_VLAN_TAG,
+ ELEM_VLAN_PROTOCOL,
+ ELEM_VLAN_PCP,
+ ELEM_PARENT_IFNAME,
+};
+
+#define MAX_STACK 64
+
+struct ifxml_cfg {
+ char ifname[IFNAMSIZ];
+ bool ifname_set;
+ char *argv[MAX_ARGS];
+ int argc;
+ char inet_addr[128];
+ char inet_dst[128];
+ char inet_netmask[128];
+ char inet_broadcast[128];
+ char inet6_addr[128];
+ char inet6_dst[128];
+ char inet6_prefixlen[16];
+};
+
+struct ifxml_addr {
+ char inet_addr[128];
+ char inet6_addr[128];
+ char dst_addr[128];
+ char netmask[128];
+ char broadcast[128];
+ char prefixlen[16];
+};
+
+struct ifxml_bridge_member {
+ char name[IFNAMSIZ];
+ char priority[16];
+ char path_cost[32];
+ char vlan_proto[16];
+};
+
+#define MAX_BRIDGE_MEMBERS 256
+
+struct ifxml_bridge {
+ struct ifxml_bridge_member members[MAX_BRIDGE_MEMBERS];
+ int num_members;
+ char priority[16];
+ char hellotime[16];
+ char fwddelay[16];
+ char maxage[16];
+ char holdcnt[16];
+ char stp_proto[16];
+ char maxaddr[16];
+ char timeout[16];
+};
+
+struct ifxml_lagg_port {
+ char name[IFNAMSIZ];
+};
+
+#define MAX_LAGG_PORTS 64
+
+struct ifxml_lagg {
+ struct ifxml_lagg_port ports[MAX_LAGG_PORTS];
+ int num_ports;
+ char proto[32];
+ char hash[64];
+};
+
+struct ifxml_state {
+ XML_Parser parser;
+ if_ctx *ctx;
+ struct ifconfig_args *args;
+
+ int stack[MAX_STACK];
+ int sp;
+
+ char text[4096];
+ int text_len;
+
+ struct ifxml_cfg cfg;
+ int cfg_count;
+ struct ifxml_cfg all_cfgs[MAX_INTERFACES];
+
+ char addr_type[64];
+ char link_addr[256];
+ char media_type[64];
+ char media_subtype[64];
+ char media_option[64];
+
+ int in_addr_block;
+ struct ifxml_addr addr;
+
+ int in_bridge;
+ struct ifxml_bridge bridge;
+ int in_member;
+ struct ifxml_bridge_member cur_member;
+
+ char tunnel_src[128];
+ char tunnel_dst[128];
+
+ int is_lagg_iface;
+ struct ifxml_lagg lagg;
+ int in_lagg_port;
+ int lagg_hash_l2;
+ int lagg_hash_l3;
+ int lagg_hash_l4;
+};
+
+static int
+elem_id(const char *name)
+{
+ if (strcmp(name, "ifconfig") == 0) return ELEM_IFCONFIG;
+ if (strcmp(name, "interface") == 0) return ELEM_INTERFACE;
+ if (strcmp(name, "ifname") == 0) return ELEM_IFNAME;
+ if (strcmp(name, "interface-flags") == 0) return ELEM_IF_FLAGS;
+ if (strcmp(name, "metric") == 0) return ELEM_METRIC;
+ if (strcmp(name, "mtu") == 0) return ELEM_MTU;
+ if (strcmp(name, "description") == 0) return ELEM_DESCRIPTION;
+ if (strcmp(name, "descr") == 0) return ELEM_DESCR;
+ if (strcmp(name, "link-address") == 0) return ELEM_LINK_ADDR_PARENT;
+ if (strcmp(name, "addr-type") == 0) return ELEM_ADDR_TYPE;
+ if (strcmp(name, "link-addr") == 0) return ELEM_LINK_ADDR_VAL;
+ if (strcmp(name, "group") == 0) return ELEM_GROUP;
+ if (strcmp(name, "name") == 0) return ELEM_GROUP_NAME;
+ if (strcmp(name, "media") == 0) return ELEM_MEDIA;
+ if (strcmp(name, "type") == 0) return ELEM_MEDIA_TYPE;
+ if (strcmp(name, "subtype") == 0) return ELEM_MEDIA_SUBTYPE;
+ if (strcmp(name, "option") == 0) return ELEM_MEDIA_OPTION;
+ if (strcmp(name, "interface-capabilities") == 0) return ELEM_IF_CAP;
+ if (strcmp(name, "options") == 0) return ELEM_OPTIONS;
+ if (strcmp(name, "options-hex") == 0) return ELEM_OPTIONS_HEX;
+ if (strcmp(name, "status") == 0) return ELEM_STATUS;
+ if (strcmp(name, "fib-id") == 0) return ELEM_FIB;
+ if (strcmp(name, "tunnelfib-id") == 0) return ELEM_TUNNEL_FIB;
+ if (strcmp(name, "address") == 0) return ELEM_ADDRESS_CONTAINER;
+ if (strcmp(name, "inet-addr") == 0) return ELEM_INET_ADDR;
+ if (strcmp(name, "inet6-addr") == 0) return ELEM_INET6_ADDR;
+ if (strcmp(name, "dst-addr") == 0) return ELEM_DST_ADDR;
+ if (strcmp(name, "netmask") == 0) return ELEM_NETMASK;
+ if (strcmp(name, "broadcast") == 0) return ELEM_BROADCAST;
+ if (strcmp(name, "prefixlen") == 0) return ELEM_PREFIXLEN;
+ if (strcmp(name, "bridge") == 0) return ELEM_BRIDGE;
+ if (strcmp(name, "bridge-priority") == 0) return ELEM_BRIDGE_PRIORITY;
+ if (strcmp(name, "hellotime") == 0) return ELEM_HELLOTIME;
+ if (strcmp(name, "fwddelay") == 0) return ELEM_FWDDELAY;
+ if (strcmp(name, "maxage") == 0) return ELEM_MAXAGE;
+ if (strcmp(name, "holdcnt") == 0) return ELEM_HOLDCNT;
+ if (strcmp(name, "stp-proto") == 0) return ELEM_STP_PROTO;
+ if (strcmp(name, "maxaddr") == 0) return ELEM_MAXADDR;
+ if (strcmp(name, "timeout") == 0) return ELEM_TIMEOUT;
+ if (strcmp(name, "member") == 0) return ELEM_MEMBER;
+ if (strcmp(name, "member-name") == 0) return ELEM_MEMBER_NAME;
+ if (strcmp(name, "port-priority") == 0) return ELEM_PORT_PRIORITY;
+ if (strcmp(name, "path-cost") == 0) return ELEM_PATH_COST;
+ if (strcmp(name, "vlan-proto") == 0) return ELEM_VLAN_PROTO;
+ if (strcmp(name, "lagg-proto") == 0) return ELEM_LAGG_PROTO;
+ if (strcmp(name, "lagg-hash-l2") == 0) return ELEM_LAGG_HASH_L2;
+ if (strcmp(name, "lagg-hash-l3") == 0) return ELEM_LAGG_HASH_L3;
+ if (strcmp(name, "lagg-hash-l4") == 0) return ELEM_LAGG_HASH_L4;
+ if (strcmp(name, "laggport") == 0) return ELEM_LAGG_PORT;
+ if (strcmp(name, "tunnel-src") == 0) return ELEM_TUNNEL_SRC;
+ if (strcmp(name, "tunnel-dst") == 0) return ELEM_TUNNEL_DST;
+ if (strcmp(name, "vlantag") == 0) return ELEM_VLAN_TAG;
+ if (strcmp(name, "vlan-protocol") == 0) return ELEM_VLAN_PROTOCOL;
+ if (strcmp(name, "vlanpcp") == 0) return ELEM_VLAN_PCP;
+ if (strcmp(name, "parent-ifname") == 0) return ELEM_PARENT_IFNAME;
+ return ELEM_NONE;
+}
+
+static void
+push_elem(struct ifxml_state *st, int id)
+{
+ if (st->sp < MAX_STACK)
+ st->stack[st->sp++] = id;
+}
+
+static int
+pop_elem(struct ifxml_state *st)
+{
+ if (st->sp > 0)
+ return st->stack[--st->sp];
+ return ELEM_NONE;
+}
+
+static int
+cur_elem(struct ifxml_state *st)
+{
+ if (st->sp > 0)
+ return st->stack[st->sp - 1];
+ return ELEM_NONE;
+}
+
+static void
+reset_text(struct ifxml_state *st)
+{
+ st->text[0] = '\0';
+ st->text_len = 0;
+}
+
+static void
+add_text(struct ifxml_state *st, const XML_Char *s, int len)
+{
+ int space = (int)sizeof(st->text) - st->text_len - 1;
+ if (len > space)
+ len = space;
+ if (len > 0) {
+ memcpy(st->text + st->text_len, s, len);
+ st->text_len += len;
+ st->text[st->text_len] = '\0';
+ }
+}
+
+static const char *
+get_text(struct ifxml_state *st)
+{
+ return st->text;
+}
+
+static void
+cfg_add_arg(struct ifxml_cfg *cfg, const char *arg)
+{
+ if (cfg->argc >= MAX_ARGS - 1)
+ errx(1, "too many arguments in XML interface config");
+ cfg->argv[cfg->argc++] = __DECONST(char *, arg);
+}
+
+static void
+cfg_add_arg_kv(struct ifxml_cfg *cfg, const char *key, const char *val)
+{
+ char *v;
+
+ cfg_add_arg(cfg, key);
+ v = strdup(val);
+ if (v == NULL)
+ err(1, "strdup");
+ cfg_add_arg(cfg, v);
+}
+
+static void
+cfg_reset(struct ifxml_cfg *cfg)
+{
+ cfg->ifname[0] = '\0';
+ cfg->ifname_set = false;
+ cfg->argc = 0;
+ cfg->inet_addr[0] = '\0';
+ cfg->inet_dst[0] = '\0';
+ cfg->inet_netmask[0] = '\0';
+ cfg->inet_broadcast[0] = '\0';
+ cfg->inet6_addr[0] = '\0';
+ cfg->inet6_dst[0] = '\0';
+ cfg->inet6_prefixlen[0] = '\0';
+}
+
+static const char *
+flag_name_to_cmd(const char *name)
+{
+ if (strcmp(name, "UP") == 0) return "up";
+ if (strcmp(name, "DEBUG") == 0) return "debug";
+ if (strcmp(name, "PROMISC") == 0) return "promisc";
+ if (strcmp(name, "ALLMULTI") == 0) return "allmulti";
+ if (strcmp(name, "MONITOR") == 0) return "monitor";
+ if (strcmp(name, "STATICARP") == 0) return "staticarp";
+ if (strcmp(name, "STICKYARP") == 0) return "stickyarp";
+ if (strcmp(name, "LINK0") == 0) return "link0";
+ if (strcmp(name, "LINK1") == 0) return "link1";
+ if (strcmp(name, "LINK2") == 0) return "link2";
+ if (strcmp(name, "NOARP") == 0) return "arp";
+ if (strcmp(name, "PPROMISC") == 0) return "promisc";
+ return NULL;
+}
+
+
+static void
+build_apply_argv(struct ifxml_cfg *cfg, int iscreate,
+ const char *const *af_args, int af_argc,
+ const struct afswtch *afp)
+{
+ struct ifconfig_args _args = {};
+ struct ifconfig_context _ctx = {};
+ char *combined[MAX_ARGS];
+ int comboc = 0;
+
+ if (iscreate) {
+ combined[comboc++] = __DECONST(char *, "create");
+ /* iscreate only on first call, reset for subsequent */
+ iscreate = 0;
+ }
+ for (int i = 0; i < cfg->argc; i++)
+ combined[comboc++] = cfg->argv[i];
+ for (int i = 0; i < af_argc; i++)
+ combined[comboc++] = __DECONST(char *, af_args[i]);
+
+ if (comboc == 0)
+ return;
+
+ if (ifconfig_xml_dry_run) {
+ printf("dry-run: %s", cfg->ifname);
+ for (int i = 0; i < comboc; i++)
+ printf(" %s", combined[i]);
+ printf("\n");
+ return;
+ }
+
+ _args.argc = comboc;
+ _args.argv = combined;
+ _args.ifname = cfg->ifname;
+ _ctx.args = &_args;
+ _ctx.ifname = cfg->ifname;
+ _ctx.io_s = -1;
+ _ctx.io_ss = NULL;
+
+ ifmaybeload(&_args, cfg->ifname);
+ ifconfig_ioctl(&_ctx, comboc > 0 && strcmp(combined[0], "create") == 0, afp);
+}
+
+static void
+apply_one_iface(struct ifxml_cfg *cfg)
+{
+ uint32_t ifindex;
+ int need_create = 0;
+
+ if (!cfg->ifname_set || cfg->ifname[0] == '\0')
+ return;
+
+ ifindex = if_nametoindex(cfg->ifname);
+ if (ifindex == 0)
+ need_create = 1;
+
+ if (need_create) {
+ const char *p = cfg->ifname;
+ if (strncmp(p, "epair", 5) == 0) {
+ p += 5;
+ while (*p >= '0' && *p <= '9')
+ p++;
+ if (*p == 'b' && *(p + 1) == '\0')
+ need_create = 0;
+ }
+ }
+
+ if (!need_create && cfg->argc == 0 &&
+ cfg->inet_addr[0] == '\0' &&
+ cfg->inet6_addr[0] == '\0')
+ return;
+
+ if (cfg->inet_addr[0] == '\0' && cfg->inet6_addr[0] == '\0') {
+ build_apply_argv(cfg, need_create, NULL, 0, NULL);
+ return;
+ }
+
+ if (cfg->inet_addr[0] != '\0') {
+ const char *af_extra[16];
+ int aec = 0;
+ af_extra[aec++] = cfg->inet_addr;
+ if (cfg->inet_dst[0] != '\0')
+ af_extra[aec++] = cfg->inet_dst;
+ if (cfg->inet_netmask[0] != '\0') {
+ af_extra[aec++] = "netmask";
+ af_extra[aec++] = cfg->inet_netmask;
+ }
+ if (cfg->inet_broadcast[0] != '\0') {
+ af_extra[aec++] = "broadcast";
+ af_extra[aec++] = cfg->inet_broadcast;
+ }
+ build_apply_argv(cfg, need_create, af_extra, aec, NULL);
+ need_create = 0;
+ }
+
+ if (cfg->inet6_addr[0] != '\0') {
+ const char *af_extra[16];
+ int aec = 0;
+ af_extra[aec++] = cfg->inet6_addr;
+ if (cfg->inet6_dst[0] != '\0')
+ af_extra[aec++] = cfg->inet6_dst;
+ if (cfg->inet6_prefixlen[0] != '\0') {
+ af_extra[aec++] = "prefixlen";
+ af_extra[aec++] = cfg->inet6_prefixlen;
+ }
+ build_apply_argv(cfg, need_create, af_extra, aec,
+ af_getbyfamily(AF_INET6));
+ }
+}
+
+static void
+flush_cfg(struct ifxml_state *st)
+{
+ if (st->cfg.ifname_set && st->cfg_count < MAX_INTERFACES)
+ st->all_cfgs[st->cfg_count++] = st->cfg;
+}
+
+static void
+apply_all_ifaces(struct ifxml_state *st)
+{
+ for (int i = 0; i < st->cfg_count; i++)
+ apply_one_iface(&st->all_cfgs[i]);
+}
+
+static void XMLCALL
+start_elem(void *userData, const XML_Char *name, const XML_Char **atts)
+{
+ struct ifxml_state *st = userData;
+ int id = elem_id(name);
+
+ push_elem(st, id);
+ reset_text(st);
+
+ switch (id) {
+ case ELEM_INTERFACE:
+ cfg_reset(&st->cfg);
+ memset(st->addr_type, 0, sizeof(st->addr_type));
+ memset(st->link_addr, 0, sizeof(st->link_addr));
+ memset(st->media_type, 0, sizeof(st->media_type));
+ memset(st->media_subtype, 0, sizeof(st->media_subtype));
+ memset(st->media_option, 0, sizeof(st->media_option));
+ memset(st->tunnel_src, 0, sizeof(st->tunnel_src));
+ memset(st->tunnel_dst, 0, sizeof(st->tunnel_dst));
+ st->in_addr_block = 0;
+ memset(&st->addr, 0, sizeof(st->addr));
+ st->in_bridge = 0;
+ memset(&st->bridge, 0, sizeof(st->bridge));
+ st->in_member = 0;
+ memset(&st->cur_member, 0, sizeof(st->cur_member));
+ st->is_lagg_iface = 0;
+ memset(&st->lagg, 0, sizeof(st->lagg));
+ st->in_lagg_port = 0;
+ st->lagg_hash_l2 = 0;
+ st->lagg_hash_l3 = 0;
+ st->lagg_hash_l4 = 0;
+ break;
+ case ELEM_MEDIA:
+ memset(st->media_type, 0, sizeof(st->media_type));
+ memset(st->media_subtype, 0, sizeof(st->media_subtype));
+ memset(st->media_option, 0, sizeof(st->media_option));
+ break;
+ case ELEM_ADDRESS_CONTAINER:
+ st->in_addr_block = 1;
+ memset(&st->addr, 0, sizeof(st->addr));
+ break;
+ case ELEM_BRIDGE:
+ st->in_bridge = 1;
+ memset(&st->bridge, 0, sizeof(st->bridge));
+ break;
+ case ELEM_MEMBER:
+ st->in_member = 1;
+ memset(&st->cur_member, 0, sizeof(st->cur_member));
+ break;
+ case ELEM_LAGG_PORT:
+ st->in_lagg_port = 1;
+ break;
+ case ELEM_LAGG_PROTO:
+ case ELEM_LAGG_HASH_L2:
+ case ELEM_LAGG_HASH_L3:
+ case ELEM_LAGG_HASH_L4:
+ break;
+ default:
+ break;
+ }
+
+ (void)atts;
+}
+
+static void XMLCALL
+end_elem(void *userData, const XML_Char *name)
+{
+ struct ifxml_state *st = userData;
+ int id = pop_elem(st);
+ const char *txt = get_text(st);
+ int parent;
+
+ (void)name;
+
+ switch (id) {
+ case ELEM_IFNAME:
+ strlcpy(st->cfg.ifname, txt, sizeof(st->cfg.ifname));
+ st->cfg.ifname_set = true;
+ if (strncmp(txt, "lagg", 4) == 0 ||
+ strncmp(txt, "bridge", 6) == 0)
+ st->is_lagg_iface = (txt[0] == 'l');
+ break;
+ case ELEM_MTU:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "mtu", txt);
+ break;
+ case ELEM_METRIC:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "metric", txt);
+ break;
+ case ELEM_DESCRIPTION:
+ case ELEM_DESCR:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "description", txt);
+ break;
+ case ELEM_IF_FLAGS: {
+ const char *cmd = flag_name_to_cmd(txt);
+ if (cmd != NULL)
+ cfg_add_arg(&st->cfg, cmd);
+ break;
+ }
+
+ case ELEM_ADDR_TYPE:
+ strlcpy(st->addr_type, txt, sizeof(st->addr_type));
+ break;
+ case ELEM_LINK_ADDR_VAL:
+ strlcpy(st->link_addr, txt, sizeof(st->link_addr));
+ parent = cur_elem(st);
+ if (parent == ELEM_LINK_ADDR_PARENT &&
+ strcmp(st->addr_type, "ether") == 0 &&
+ st->link_addr[0] != '\0') {
+ cfg_add_arg(&st->cfg, "ether");
+ cfg_add_arg(&st->cfg, strdup(st->link_addr));
+ }
+ break;
+ case ELEM_GROUP_NAME:
+ parent = cur_elem(st);
+ if (parent == ELEM_GROUP && txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "group", txt);
+ else if (parent == ELEM_LAGG_PORT && txt[0] != '\0' &&
+ st->lagg.num_ports < MAX_LAGG_PORTS)
+ strlcpy(st->lagg.ports[st->lagg.num_ports].name, txt,
+ sizeof(st->lagg.ports[st->lagg.num_ports].name));
+ break;
+ case ELEM_MEDIA_TYPE:
+ strlcpy(st->media_type, txt, sizeof(st->media_type));
+ break;
+ case ELEM_MEDIA_SUBTYPE:
+ strlcpy(st->media_subtype, txt, sizeof(st->media_subtype));
+ break;
+ case ELEM_MEDIA_OPTION:
+ strlcpy(st->media_option, txt, sizeof(st->media_option));
+ break;
+ case ELEM_MEDIA:
+ break;
+ case ELEM_INET_ADDR:
+ if (st->in_addr_block)
+ strlcpy(st->addr.inet_addr, txt, sizeof(st->addr.inet_addr));
+ break;
+ case ELEM_INET6_ADDR:
+ if (st->in_addr_block)
+ strlcpy(st->addr.inet6_addr, txt, sizeof(st->addr.inet6_addr));
+ break;
+ case ELEM_DST_ADDR:
+ if (st->in_addr_block)
+ strlcpy(st->addr.dst_addr, txt, sizeof(st->addr.dst_addr));
+ break;
+ case ELEM_NETMASK:
+ if (st->in_addr_block)
+ strlcpy(st->addr.netmask, txt, sizeof(st->addr.netmask));
+ break;
+ case ELEM_BROADCAST:
+ if (st->in_addr_block)
+ strlcpy(st->addr.broadcast, txt, sizeof(st->addr.broadcast));
+ break;
+ case ELEM_PREFIXLEN:
+ if (st->in_addr_block)
+ strlcpy(st->addr.prefixlen, txt, sizeof(st->addr.prefixlen));
+ break;
+ case ELEM_ADDRESS_CONTAINER:
+ st->in_addr_block = 0;
+ if (st->addr.inet_addr[0] != '\0') {
+ strlcpy(st->cfg.inet_addr, st->addr.inet_addr,
+ sizeof(st->cfg.inet_addr));
+ strlcpy(st->cfg.inet_dst, st->addr.dst_addr,
+ sizeof(st->cfg.inet_dst));
+ strlcpy(st->cfg.inet_netmask, st->addr.netmask,
+ sizeof(st->cfg.inet_netmask));
+ strlcpy(st->cfg.inet_broadcast, st->addr.broadcast,
+ sizeof(st->cfg.inet_broadcast));
+ } else if (st->addr.inet6_addr[0] != '\0') {
+ strlcpy(st->cfg.inet6_addr, st->addr.inet6_addr,
+ sizeof(st->cfg.inet6_addr));
+ strlcpy(st->cfg.inet6_dst, st->addr.dst_addr,
+ sizeof(st->cfg.inet6_dst));
+ strlcpy(st->cfg.inet6_prefixlen, st->addr.prefixlen,
+ sizeof(st->cfg.inet6_prefixlen));
+ }
+ break;
+ case ELEM_FIB:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "fib", txt);
+ break;
+ case ELEM_TUNNEL_FIB:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "tunnelfib", txt);
+ break;
+ case ELEM_BRIDGE_PRIORITY:
+ strlcpy(st->bridge.priority, txt, sizeof(st->bridge.priority));
+ break;
+ case ELEM_HELLOTIME:
+ strlcpy(st->bridge.hellotime, txt, sizeof(st->bridge.hellotime));
+ break;
+ case ELEM_FWDDELAY:
+ strlcpy(st->bridge.fwddelay, txt, sizeof(st->bridge.fwddelay));
+ break;
+ case ELEM_MAXAGE:
+ strlcpy(st->bridge.maxage, txt, sizeof(st->bridge.maxage));
+ break;
+ case ELEM_HOLDCNT:
+ strlcpy(st->bridge.holdcnt, txt, sizeof(st->bridge.holdcnt));
+ break;
+ case ELEM_STP_PROTO:
+ strlcpy(st->bridge.stp_proto, txt, sizeof(st->bridge.stp_proto));
+ break;
+ case ELEM_MAXADDR:
+ strlcpy(st->bridge.maxaddr, txt, sizeof(st->bridge.maxaddr));
+ break;
+ case ELEM_TIMEOUT:
+ strlcpy(st->bridge.timeout, txt, sizeof(st->bridge.timeout));
+ break;
+ case ELEM_MEMBER_NAME:
+ if (st->in_member)
+ strlcpy(st->cur_member.name, txt, sizeof(st->cur_member.name));
+ break;
+ case ELEM_PORT_PRIORITY:
+ if (st->in_member)
+ strlcpy(st->cur_member.priority, txt, sizeof(st->cur_member.priority));
+ break;
+ case ELEM_PATH_COST:
+ if (st->in_member)
+ strlcpy(st->cur_member.path_cost, txt, sizeof(st->cur_member.path_cost));
+ break;
+ case ELEM_VLAN_PROTO:
+ if (st->in_member)
+ strlcpy(st->cur_member.vlan_proto, txt, sizeof(st->cur_member.vlan_proto));
+ break;
+ case ELEM_MEMBER:
+ if (st->in_bridge && st->cur_member.name[0] != '\0' &&
+ st->bridge.num_members < MAX_BRIDGE_MEMBERS)
+ st->bridge.members[st->bridge.num_members++] = st->cur_member;
+ st->in_member = 0;
+ break;
+ case ELEM_BRIDGE:
+ st->in_bridge = 0;
+ if (st->bridge.priority[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "priority", st->bridge.priority);
+ if (st->bridge.hellotime[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "hellotime", st->bridge.hellotime);
+ if (st->bridge.fwddelay[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "fwddelay", st->bridge.fwddelay);
+ if (st->bridge.maxage[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "maxage", st->bridge.maxage);
+ if (st->bridge.holdcnt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "holdcnt", st->bridge.holdcnt);
+ if (st->bridge.stp_proto[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "proto", st->bridge.stp_proto);
+ if (st->bridge.maxaddr[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "maxaddr", st->bridge.maxaddr);
+ if (st->bridge.timeout[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "timeout", st->bridge.timeout);
+ for (int i = 0; i < st->bridge.num_members; i++)
+ cfg_add_arg_kv(&st->cfg, "addm", st->bridge.members[i].name);
+ break;
+ case ELEM_TUNNEL_SRC:
+ strlcpy(st->tunnel_src, txt, sizeof(st->tunnel_src));
+ break;
+ case ELEM_TUNNEL_DST:
+ strlcpy(st->tunnel_dst, txt, sizeof(st->tunnel_dst));
+ break;
+ case ELEM_VLAN_TAG:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "vlan", txt);
+ break;
+ case ELEM_VLAN_PROTOCOL:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "vlanproto", txt);
+ break;
+ case ELEM_VLAN_PCP:
+ if (txt[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "vlanpcp", txt);
+ break;
+ case ELEM_PARENT_IFNAME:
+ if (txt[0] != '\0' && strncmp(st->cfg.ifname, "wlan", 4) == 0)
+ cfg_add_arg_kv(&st->cfg, "wlandev", txt);
+ break;
+ case ELEM_LAGG_PROTO:
+ if (st->is_lagg_iface)
+ strlcpy(st->lagg.proto, txt, sizeof(st->lagg.proto));
+ break;
+ case ELEM_LAGG_HASH_L2:
+ if (st->is_lagg_iface)
+ st->lagg_hash_l2 = 1;
+ break;
+ case ELEM_LAGG_HASH_L3:
+ if (st->is_lagg_iface)
+ st->lagg_hash_l3 = 1;
+ break;
+ case ELEM_LAGG_HASH_L4:
+ if (st->is_lagg_iface)
+ st->lagg_hash_l4 = 1;
+ break;
+ case ELEM_LAGG_PORT:
+ st->in_lagg_port = 0;
+ if (st->lagg.ports[st->lagg.num_ports].name[0] != '\0')
+ st->lagg.num_ports++;
+ break;
+ case ELEM_INTERFACE:
+ if (st->lagg.proto[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "laggproto", st->lagg.proto);
+ if (st->lagg_hash_l2 || st->lagg_hash_l3 || st->lagg_hash_l4) {
+ char hashbuf[32];
+ hashbuf[0] = '\0';
+ if (st->lagg_hash_l2)
+ strlcat(hashbuf, "l2", sizeof(hashbuf));
+ if (st->lagg_hash_l3) {
+ if (hashbuf[0] != '\0')
+ strlcat(hashbuf, ",", sizeof(hashbuf));
+ strlcat(hashbuf, "l3", sizeof(hashbuf));
+ }
+ if (st->lagg_hash_l4) {
+ if (hashbuf[0] != '\0')
+ strlcat(hashbuf, ",", sizeof(hashbuf));
+ strlcat(hashbuf, "l4", sizeof(hashbuf));
+ }
+ if (hashbuf[0] != '\0')
+ cfg_add_arg_kv(&st->cfg, "lagghash", hashbuf);
+ }
+ for (int i = 0; i < st->lagg.num_ports; i++)
+ cfg_add_arg_kv(&st->cfg, "laggport", st->lagg.ports[i].name);
+ if (st->tunnel_src[0] != '\0' || st->tunnel_dst[0] != '\0') {
+ cfg_add_arg(&st->cfg, "tunnel");
+ cfg_add_arg(&st->cfg, strdup(
+ st->tunnel_src[0] != '\0' ? st->tunnel_src : "0.0.0.0"));
+ cfg_add_arg(&st->cfg, strdup(
+ st->tunnel_dst[0] != '\0' ? st->tunnel_dst : "0.0.0.0"));
+ }
+ flush_cfg(st);
+ break;
+ default:
+ break;
+ }
+}
+
+static void XMLCALL
+char_data(void *userData, const XML_Char *s, int len)
+{
+ struct ifxml_state *st = userData;
+ add_text(st, s, len);
+}
+
+int
+ifconfig_xml_apply(if_ctx *ctx, const char *filename)
+{
+ char buf[65536];
+ struct ifxml_state st;
+ int fd;
+ ssize_t n;
+ enum XML_Status xstatus;
+
+ memset(&st, 0, sizeof(st));
+ st.ctx = ctx;
+ if (ctx != NULL)
+ st.args = ctx->args;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ err(1, "cannot open %s", filename);
+
+ st.parser = XML_ParserCreate(NULL);
+ if (st.parser == NULL)
+ errx(1, "XML_ParserCreate failed");
+
+ XML_SetUserData(st.parser, &st);
+ XML_SetElementHandler(st.parser, start_elem, end_elem);
+ XML_SetCharacterDataHandler(st.parser, char_data);
+
+ while ((n = read(fd, buf, sizeof(buf))) > 0) {
+ xstatus = XML_Parse(st.parser, buf, (int)n, 0);
+ if (xstatus == XML_STATUS_ERROR) {
+ enum XML_Error xerr = XML_GetErrorCode(st.parser);
+ warnx("XML parse error in %s at line %lu: %s",
+ filename,
+ (unsigned long)XML_GetCurrentLineNumber(st.parser),
+ XML_ErrorString(xerr));
+ break;
+ }
+ }
+ xstatus = XML_Parse(st.parser, buf, 0, 1);
+ if (xstatus == XML_STATUS_ERROR) {
+ enum XML_Error xerr = XML_GetErrorCode(st.parser);
+ warnx("XML final parse error in %s: %s",
+ filename, XML_ErrorString(xerr));
+ }
+
+ XML_ParserFree(st.parser);
+ close(fd);
+
+ apply_all_ifaces(&st);
+ return (0);
+}
+
+static void
+ifxml_cb(const char *arg)
+{
+ ifconfig_xml_apply(NULL, arg);
+ exit(exit_code);
+}
+
+static void
+dryrun_cb(const char *arg __unused)
+{
+ ifconfig_xml_dry_run = 1;
+}
+
+static struct option xml_opt = {
+ .opt = "x:",
+ .opt_usage = "[-x xmlfile]",
+ .cb = ifxml_cb,
+};
+
+static struct option xml_dryrun_opt = {
+ .opt = "N",
+ .opt_usage = "[-N]",
+ .cb = dryrun_cb,
+};
+
+static __constructor void
+ifxml_ctor(void)
+{
+ opt_register(&xml_dryrun_opt);
+ opt_register(&xml_opt);
+}
diff --git a/sbin/ifconfig/ifconfig_xml.h b/sbin/ifconfig/ifconfig_xml.h
new file mode 100644
index 00000000000..480f7dd228c
--- /dev/null
+++ b/sbin/ifconfig/ifconfig_xml.h
@@ -0,0 +1,5 @@
+#pragma once
+
+extern int ifconfig_xml_dry_run;
+
+int ifconfig_xml_apply(if_ctx *ctx, const char *filename);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment