Created
July 30, 2026 13:22
-
-
Save paigeadelethompson/e295b2d36f7e536595d6c32c760b2a1f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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