commit dd054fead9185c4e49b5985abed6b555cd29ecbe from: Stefan Sperling date: Fri Sep 15 14:56:28 2023 UTC Fix dhcpd handling with respect to rdoamins Run dhcpd in the correct rdomain when rdomain is != 0 and start dhcpd with a list of interfaces on its command line. All interfaces must be in the same rdomain; dhcpd will then set its own rdomain accordingly. Use a separate DHCP lease database per rdomain just in case a given MAC and IP tuple appears in multiple rdomains. earlier version ok + testing by Tom commit - 85c6a750079d9a9f6d6d41f565f5eaaa6e11144c commit + dd054fead9185c4e49b5985abed6b555cd29ecbe blob - 4f3fda97b1409dd8b3ad816ebd6d0850f149bb4d blob + 94a5f8d21bdd5c4824a9d3ee370631ebe3743778 --- ctl.c +++ ctl.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "externs.h" #include "editing.h" #include "ctl.h" @@ -87,6 +88,7 @@ void edit_crontab(char *); void install_crontab(char *); void edit_motd(char *); void call_editor(char *, char **, char *); +void start_dhcpd(char *, char *, char *, char *, char *); int edit_file(char *, mode_t, char *, char **); void ctl_symlink(char *, char *, char *); int rule_writeline(char *, mode_t, char *); @@ -437,8 +439,8 @@ struct ctl ctl_nppp[] = { /* dhcpd */ struct ctl2 ctl_dhcp[] = { { "enable", "enable DHCPd daemon", - { DHCPD, "-c", REQTEMP, "-l", DHCPLEASES, NULL }, { NULL, }, NULL, - DB_X_ENABLE, T_EXEC }, + { DHCPD, "-c", REQTEMP, "-l", DHCPLEASES, NULL }, { NULL, }, + start_dhcpd, DB_X_ENABLE, T_HANDLER }, { "disable", "disable DHCPd daemon", { PKILL, table, "dhcpd", NULL }, { NULL, }, NULL, DB_X_DISABLE, T_EXEC }, @@ -1179,4 +1181,86 @@ rmtemp(char *file) if (errno != ENOENT) printf("%% Unable to remove temporary file %s: %s\n", file, strerror(errno)); +} + +void +start_dhcpd(char *arg0, char *arg1, char *arg2, char *arg3, char *arg4) +{ + struct if_nameindex *ifn_list, *ifnp; + char **dhcpd_args = NULL; + size_t niface = 0; + int ifs, i; + char leasedb[PATH_MAX]; + + /* + * For rdomains other than zero dhcpd(8) expects a list of + * interfaces on its command line. If no interface arguments + * are given then dhcpd will move itself into rdomain zero + * so we really must specify a list here. + * + * All named interfaces must be part of the same rdomain. We + * provide the list of all interfaces in our current rdomain. + * dhcpd will listen on any with matching subnets in dhcpd.conf. + */ + if ((ifn_list = if_nameindex()) == NULL) { + printf("%% %s: if_nameindex failed\n", __func__); + return; + } + + if ((ifs = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + printf("%% %s socket: %s\n", __func__, strerror(errno)); + goto done; + } + + for (ifnp = ifn_list; ifnp->if_name != NULL; ifnp++) { + int flags, rdomain; + + flags = get_ifflags(ifnp->if_name, ifs); + if ((flags & IFF_LOOPBACK) || + (flags & IFF_POINTOPOINT) || + (!(flags & IFF_BROADCAST))) + continue; + + rdomain = get_rdomain(ifs, ifnp->if_name); + if (rdomain == cli_rtable) + niface++; + } + + dhcpd_args = calloc(5 + niface + 1, sizeof(char *)); + if (dhcpd_args == NULL) { + printf("%% calloc: %s\n", strerror(errno)); + goto done; + } + + i = 0; + dhcpd_args[i++] = arg0; /* dhcpd */ + dhcpd_args[i++] = arg1; /* -c */ + dhcpd_args[i++] = arg2; /* dhcpd.conf */ + dhcpd_args[i++] = arg3; /* -l */ + if (cli_rtable != 0) { + snprintf(leasedb, sizeof(leasedb), "%s.%d", + arg4, cli_rtable); + dhcpd_args[i++] = leasedb; /* rdomain's leasedb */ + } else + dhcpd_args[i++] = arg4; /* default leasedb */ + + for (ifnp = ifn_list; ifnp->if_name != NULL; ifnp++) { + int flags, rdomain; + + flags = get_ifflags(ifnp->if_name, ifs); + if ((flags & IFF_LOOPBACK) || + (flags & IFF_POINTOPOINT) || + (!(flags & IFF_BROADCAST))) + continue; + + rdomain = get_rdomain(ifs, ifnp->if_name); + if (rdomain == cli_rtable) + dhcpd_args[i++] = ifnp->if_name; + } + dhcpd_args[i] = NULL; + + cmdargs(arg0, dhcpd_args); +done: + if_freenameindex(ifn_list); + free(dhcpd_args); }