|
| 1 | +This patch adds the following functionality to dhcrelay in isc-dhcp v4.3.1-6: |
| 2 | +* Add customizable Circuit ID and Remote ID fields |
| 3 | +* Support for obtaining name of physical interface of interfaces that are part of a bridge interface |
| 4 | + |
| 5 | +diff -ruN a/isc-dhcp/relay/dhcrelay.c b/isc-dhcp/relay/dhcrelay.c |
| 6 | +--- a/isc-dhcp/relay/dhcrelay.c 2014-08-06 22:35:02.000000000 +0000 |
| 7 | ++++ b/isc-dhcp/relay/dhcrelay.c 2017-06-08 21:39:53.856192546 +0000 |
| 8 | +@@ -73,6 +73,8 @@ |
| 9 | + did not match any known circuit ID. */ |
| 10 | + int missing_circuit_id = 0; /* Circuit ID option in matching RAI option |
| 11 | + was missing. */ |
| 12 | ++const char *agent_circuit_id_fmt = NULL; /* Circuit ID custom format string. */ |
| 13 | ++const char *agent_remote_id_fmt = NULL; /* Remote ID custom format string. */ |
| 14 | + int max_hop_count = 10; /* Maximum hop count */ |
| 15 | + |
| 16 | + #ifdef DHCPv6 |
| 17 | +@@ -140,9 +142,19 @@ |
| 18 | + static const char url[] = |
| 19 | + "For info, please visit https://www.isc.org/software/dhcp/"; |
| 20 | + |
| 21 | ++#define DHCRELAY_OPTION82_USAGE \ |
| 22 | ++"circuit_id/remote_id interpreted sequences are:\n" \ |
| 23 | ++"\n" \ |
| 24 | ++" %%%% A single %%\n" \ |
| 25 | ++" %%h Hostname of device\n" \ |
| 26 | ++" %%p Name of interface that generated the request\n" \ |
| 27 | ++" %%P Hardware address of interface that generated the request\n" \ |
| 28 | ++" %%C Client hardware address\n" \ |
| 29 | ++" %%I DHCP relay agent IP Address\n" \ |
| 30 | ++ |
| 31 | + #ifdef DHCPv6 |
| 32 | + #define DHCRELAY_USAGE \ |
| 33 | +-"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\ |
| 34 | ++"Usage: dhcrelay [-4] [-d] [-q] [-a <circuit_id> <remote_id>] [-D]\n"\ |
| 35 | + " [-A <length>] [-c <hops>] [-p <port>]\n" \ |
| 36 | + " [-pf <pid-file>] [--no-pid]\n"\ |
| 37 | + " [-m append|replace|forward|discard]\n" \ |
| 38 | +@@ -154,14 +166,15 @@ |
| 39 | + " -l lower0 [ ... -l lowerN]\n" \ |
| 40 | + " -u upper0 [ ... -u upperN]\n" \ |
| 41 | + " lower (client link): [address%%]interface[#index]\n" \ |
| 42 | +-" upper (server link): [address%%]interface" |
| 43 | ++" upper (server link): [address%%]interface\n\n" DHCRELAY_OPTION82_USAGE |
| 44 | + #else |
| 45 | + #define DHCRELAY_USAGE \ |
| 46 | +-"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \ |
| 47 | +-" [-pf <pid-file>] [--no-pid]\n" \ |
| 48 | ++"Usage: dhcrelay [-d] [-q] [-a <circuit_id> <remote_id>] [-D]\n" \ |
| 49 | ++" [-A <length>] [-c <hops>] [-p <port>]\n" \ |
| 50 | ++" [-pf <pid-file>] [--no-pid]\n"\ |
| 51 | + " [-m append|replace|forward|discard]\n" \ |
| 52 | + " [-i interface0 [ ... -i interfaceN]\n" \ |
| 53 | +-" server0 [ ... serverN]\n\n" |
| 54 | ++" server0 [ ... serverN]\n\n" DHCRELAY_OPTION82_USAGE |
| 55 | + #endif |
| 56 | + |
| 57 | + static void usage() { |
| 58 | +@@ -287,6 +300,15 @@ |
| 59 | + local_family_set = 1; |
| 60 | + local_family = AF_INET; |
| 61 | + #endif |
| 62 | ++ if (++i == argc) |
| 63 | ++ usage(); |
| 64 | ++ |
| 65 | ++ if (argv[i] != NULL && argv[i][0] != '-') |
| 66 | ++ agent_circuit_id_fmt = argv[i++]; |
| 67 | ++ |
| 68 | ++ if (argv[i] != NULL && argv[i][0] != '-') |
| 69 | ++ agent_remote_id_fmt = argv[i]; |
| 70 | ++ |
| 71 | + add_agent_options = 1; |
| 72 | + } else if (!strcmp(argv[i], "-A")) { |
| 73 | + #ifdef DHCPv6 |
| 74 | +@@ -937,6 +959,166 @@ |
| 75 | + return (-1); |
| 76 | + } |
| 77 | + |
| 78 | ++static int |
| 79 | ++_bridgefdbquery(const char *hwAddr, char *interface, int *vlanid) { |
| 80 | ++ |
| 81 | ++#define xstr(s) str(s) |
| 82 | ++#define str(s) #s |
| 83 | ++#define FDB_STRING_LEN 100 |
| 84 | ++#define FDB_BUFFER_LEN (FDB_STRING_LEN + 1) |
| 85 | ++ |
| 86 | ++/* |
| 87 | ++ * Format for sscanf() to read the 1st, 3th, and 5th |
| 88 | ++ * space-delimited fields |
| 89 | ++ * |
| 90 | ++ * bridge fdb show output |
| 91 | ++ * 6c:64:1a:00:06:13 dev swp35 vlan 0 master bridge permanent |
| 92 | ++ */ |
| 93 | ++#define FDB_LINE_FORMAT "%" xstr(FDB_STRING_LEN) "s %*s " \ |
| 94 | ++ "%" xstr(FDB_STRING_LEN) "s %*s %d %*s" |
| 95 | ++ |
| 96 | ++ char cmdstr[FDB_BUFFER_LEN]; |
| 97 | ++ char buf[FDB_BUFFER_LEN]; |
| 98 | ++ char macAddr[FDB_BUFFER_LEN]; |
| 99 | ++ |
| 100 | ++ if ((interface == NULL) || (vlanid == NULL)) { |
| 101 | ++ return 0; |
| 102 | ++ } |
| 103 | ++ sprintf(cmdstr, "bridge fdb show | grep -m 1 %s", hwAddr); |
| 104 | ++ FILE *cmd = popen(cmdstr, "r"); |
| 105 | ++ |
| 106 | ++ if (cmd != NULL) { |
| 107 | ++ while (fgets(buf, sizeof(buf), cmd)) { |
| 108 | ++ sscanf(buf, FDB_LINE_FORMAT, macAddr, interface, vlanid); |
| 109 | ++ log_debug ("bridgefdbquery: macAddr:%s interface: %s vlanid %d", |
| 110 | ++ macAddr, |
| 111 | ++ interface, *vlanid); |
| 112 | ++ } |
| 113 | ++ pclose(cmd); |
| 114 | ++ return 0; |
| 115 | ++ } |
| 116 | ++ |
| 117 | ++ return -1; |
| 118 | ++} |
| 119 | ++ |
| 120 | ++/* |
| 121 | ++ * Format the message that will be used by circuit_id and remote_id |
| 122 | ++ */ |
| 123 | ++static int |
| 124 | ++format_relay_agent_rfc3046_msg(struct interface_info *ip, struct dhcp_packet *packet, |
| 125 | ++ const char *format, char *msg, size_t msgn) { |
| 126 | ++ size_t len = 0; |
| 127 | ++ char hostname[HOST_NAME_MAX + 1] = { 0 }; |
| 128 | ++ char ifname[IFNAMSIZ + 1] = { 0 }; |
| 129 | ++ char *buf = msg; |
| 130 | ++ |
| 131 | ++ for ( ; format && *format && len < msgn; ++format) { |
| 132 | ++ size_t strn = 0; |
| 133 | ++ const char *str = NULL; |
| 134 | ++ |
| 135 | ++ if (*format == '%') { |
| 136 | ++ switch (*++format) { |
| 137 | ++ case '\0': |
| 138 | ++ --format; |
| 139 | ++ break; |
| 140 | ++ |
| 141 | ++ case '%': /* A literal '%' */ |
| 142 | ++ str = "%"; |
| 143 | ++ break; |
| 144 | ++ |
| 145 | ++ case 'h': /* Hostname */ |
| 146 | ++ gethostname(hostname, HOST_NAME_MAX); |
| 147 | ++ str = hostname; |
| 148 | ++ break; |
| 149 | ++ |
| 150 | ++ case 'p': /* Name of interface that we received the request from */ |
| 151 | ++ /* |
| 152 | ++ * Query FDB to identify the exact physical interface only when source MAC address |
| 153 | ++ * is present and '20: DHCP relay agent IP address' (giaddr) is not present |
| 154 | ++ */ |
| 155 | ++ if (packet->htype && !packet->giaddr.s_addr) { |
| 156 | ++ int ret = 0, vlanid = 0; |
| 157 | ++ |
| 158 | ++ ret = _bridgefdbquery(print_hw_addr(packet->htype, packet->hlen, packet->chaddr), |
| 159 | ++ ip->name, |
| 160 | ++ &vlanid); |
| 161 | ++ |
| 162 | ++ if (ret < 0) { |
| 163 | ++ log_debug("MAC Address: %s (interface:%s vlan:%d) not found in bridge fdb show", |
| 164 | ++ print_hw_addr (packet->htype, packet->hlen, packet->chaddr), |
| 165 | ++ ip->name, |
| 166 | ++ vlanid); |
| 167 | ++ strncpy(ifname, ip->name, IFNAMSIZ); |
| 168 | ++ } |
| 169 | ++ else if (strlen(ip->name) > 0) { |
| 170 | ++ char cmdstr[256] = { 0 }; |
| 171 | ++ char cmdout[256] = { 0 }; |
| 172 | ++ |
| 173 | ++ log_debug("Adding option 82 interface name for MAC Address: %s as %s", |
| 174 | ++ print_hw_addr (packet->htype, packet->hlen, packet->chaddr), |
| 175 | ++ ip->name); |
| 176 | ++ |
| 177 | ++ // Translate SONiC interface name to vendor alias |
| 178 | ++ sprintf(cmdstr, "sonic-cfggen -m /etc/sonic/minigraph.xml -v \"minigraph_ports['%s'].alias\"", ip->name); |
| 179 | ++ |
| 180 | ++ FILE *cmd = popen(cmdstr, "r"); |
| 181 | ++ |
| 182 | ++ if (cmd != NULL) { |
| 183 | ++ while (fgets(cmdout, sizeof(cmdout), cmd)) { |
| 184 | ++ // Strip any trailing newline |
| 185 | ++ if (cmdout[strlen(cmdout) - 1] == '\n') |
| 186 | ++ cmdout[strlen(cmdout) - 1] = '\0'; |
| 187 | ++ |
| 188 | ++ log_debug ("Retrieved alias %s for interface %s", buf, ip->name); |
| 189 | ++ } |
| 190 | ++ |
| 191 | ++ pclose(cmd); |
| 192 | ++ } |
| 193 | ++ |
| 194 | ++ strncpy(ifname, cmdout, IFNAMSIZ); |
| 195 | ++ } |
| 196 | ++ |
| 197 | ++ str = ifname; |
| 198 | ++ } |
| 199 | ++ break; |
| 200 | ++ |
| 201 | ++ case 'P': /* Physical address of interface that we received the request from */ |
| 202 | ++ str = print_hw_addr(ip->hw_address.hbuf[0], ip->hw_address.hlen - 1, &ip->hw_address.hbuf[1]); |
| 203 | ++ break; |
| 204 | ++ |
| 205 | ++ case 'C': /* 24: Client hardware address */ |
| 206 | ++ str = print_hw_addr(packet->htype, packet->hlen, packet->chaddr); |
| 207 | ++ break; |
| 208 | ++ |
| 209 | ++ case 'I': /* 20: DHCP relay agent IP address */ |
| 210 | ++ str = inet_ntoa(packet->giaddr); |
| 211 | ++ break; |
| 212 | ++ |
| 213 | ++ default: |
| 214 | ++ log_error("Option %%%c is unrecognized and will not be formatted!", *format); |
| 215 | ++ continue; |
| 216 | ++ } |
| 217 | ++ |
| 218 | ++ if (str) |
| 219 | ++ strn = strlen(str); |
| 220 | ++ } else { |
| 221 | ++ str = format; |
| 222 | ++ strn += 1; |
| 223 | ++ } |
| 224 | ++ |
| 225 | ++ // Do we have room? |
| 226 | ++ if ((strn+len) > msgn) { |
| 227 | ++ return 0; |
| 228 | ++ } |
| 229 | ++ |
| 230 | ++ memcpy(buf+len, str, strn); |
| 231 | ++ len += strn; |
| 232 | ++ } |
| 233 | ++ |
| 234 | ++ return len; |
| 235 | ++} |
| 236 | ++ |
| 237 | ++ |
| 238 | + /* |
| 239 | + * Examine a packet to see if it's a candidate to have a Relay |
| 240 | + * Agent Information option tacked onto its tail. If it is, tack |
| 241 | +@@ -948,6 +1130,8 @@ |
| 242 | + int is_dhcp = 0, mms; |
| 243 | + unsigned optlen; |
| 244 | + u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; |
| 245 | ++ char circuit_id_buf[255] = { '\0', }; |
| 246 | ++ char remote_id_buf[255] = { '\0', }; |
| 247 | + |
| 248 | + /* If we're not adding agent options to packets, we can skip |
| 249 | + this. */ |
| 250 | +@@ -1077,6 +1261,38 @@ |
| 251 | + op = sp; |
| 252 | + #endif |
| 253 | + |
| 254 | ++ /* option82: custom string for circuit_id */ |
| 255 | ++ if (agent_circuit_id_fmt) { |
| 256 | ++ size_t len = 0; |
| 257 | ++ |
| 258 | ++ len = format_relay_agent_rfc3046_msg(ip, packet, agent_circuit_id_fmt, |
| 259 | ++ circuit_id_buf, sizeof(circuit_id_buf)); |
| 260 | ++ |
| 261 | ++ if (len > 0) { |
| 262 | ++ ip->circuit_id = (uint8_t *)circuit_id_buf; |
| 263 | ++ ip->circuit_id_len = len; |
| 264 | ++ |
| 265 | ++ log_debug("sending on %s option82:circuit_id='%s'(%d)", |
| 266 | ++ ip->name, (char *)ip->circuit_id, ip->circuit_id_len); |
| 267 | ++ } |
| 268 | ++ } |
| 269 | ++ |
| 270 | ++ /* option82: custom string for remote_id */ |
| 271 | ++ if (agent_remote_id_fmt) { |
| 272 | ++ size_t len = 0; |
| 273 | ++ |
| 274 | ++ len = format_relay_agent_rfc3046_msg(ip, packet, agent_remote_id_fmt, |
| 275 | ++ remote_id_buf, sizeof(remote_id_buf)); |
| 276 | ++ |
| 277 | ++ if (len > 0) { |
| 278 | ++ ip->remote_id = (uint8_t *)remote_id_buf; |
| 279 | ++ ip->remote_id_len = len; |
| 280 | ++ |
| 281 | ++ log_debug("sending on %s option82:remote_id='%s'(%d)", |
| 282 | ++ ip->name, (char *)ip->remote_id, ip->remote_id_len); |
| 283 | ++ } |
| 284 | ++ } |
| 285 | ++ |
| 286 | + /* Sanity check. Had better not ever happen. */ |
| 287 | + if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1)) |
| 288 | + log_fatal("Circuit ID length %d out of range [1-255] on " |
| 289 | + |
0 commit comments