#include <sys/socket.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/if_ether.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "dhcp.h"
#include "dhcpd.h"
#include "log.h"
struct in6_addr in6alldhcprelay = {
{{ 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x02 }}
};
struct in6_addr in6alldhcp = {
{{ 0xff, 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0, 0x03 }}
};
__dead void usage(void);
struct server_list *parse_destination(const char *);
void relay6_setup(void);
int s6fromaddr(struct sockaddr_in6 *, const char *, const char *, int);
char *print_hw_addr(int, int, unsigned char *);
const char *dhcp6type2str(uint8_t);
int relay6_pushrelaymsg(struct packet_ctx *, struct interface_info *,
uint8_t *, size_t *, size_t);
int relay6_poprelaymsg(struct packet_ctx *, struct interface_info **,
uint8_t *, size_t *);
void rai_configure(struct packet_ctx *, struct interface_info *);
void relay6_logsrcaddr(struct packet_ctx *, struct interface_info *,
uint8_t);
void relay6(struct interface_info *, void *, size_t,
struct packet_ctx *);
void mcast6_recv(struct protocol *);
int clientsd;
int serversd;
int oflag;
time_t cur_time;
struct intfq intflist;
struct serverq svlist;
struct interface_info *interfaces;
char *rai_data;
size_t rai_datalen;
uint32_t enterpriseno = OPENBSD_ENTERPRISENO;
char *remote_data;
size_t remote_datalen;
enum dhcp_relay_mode drm = DRM_LAYER3;
__dead void
usage(void)
{
extern char *__progname;
fprintf(stderr, "usage: %s [-dlov] [-E enterprise-number] "
"[-I interface-id] [-R remote-id]\n"
"\t-i interface destination ...\n",
__progname);
exit(1);
}
struct server_list *
parse_destination(const char *dest)
{
struct server_list *sp;
const char *ifname;
char buf[128];
if ((sp = calloc(1, sizeof(*sp))) == NULL)
fatal("calloc");
TAILQ_INSERT_HEAD(&svlist, sp, entry);
if ((sp->intf = iflist_getbyname(dest)) != NULL)
return sp;
ifname = strchr(dest, '%');
if (ifname == NULL)
fatalx("%s doesn't specify an output interface", dest);
if (strlcpy(buf, dest, sizeof(buf)) >= sizeof(buf))
fatalx("%s is an invalid IPv6 address", dest);
buf[ifname - dest] = 0;
if ((sp->intf = iflist_getbyname(ifname + 1)) == NULL)
fatalx("interface '%s' not found", ifname);
if (s6fromaddr(ss2sin6(&sp->to), buf,
DHCP6_SERVER_PORT_STR, 1) == -1)
fatalx("%s: unknown host", buf);
if (!IN6_IS_ADDR_LINKLOCAL(&ss2sin6(&sp->to)->sin6_addr))
sp->siteglobaladdr = 1;
return sp;
}
int
main(int argc, char *argv[])
{
int daemonize = 1, debug = 0;
const char *errp;
struct passwd *pw;
int ch;
log_init(1, LOG_DAEMON);
log_setverbose(1);
setup_iflist();
while ((ch = getopt(argc, argv, "dE:I:i:loR:v")) != -1) {
switch (ch) {
case 'd':
daemonize = 0;
break;
case 'E':
enterpriseno = strtonum(optarg, 1, UINT32_MAX, &errp);
if (errp != NULL)
fatalx("invalid enterprise number: %s", errp);
break;
case 'I':
rai_data = optarg;
rai_datalen = strlen(optarg);
if (rai_datalen == 0)
fatalx("can't use empty Interface-ID");
break;
case 'i':
if (interfaces != NULL)
usage();
interfaces = iflist_getbyname(optarg);
if (interfaces == NULL)
fatalx("interface '%s' not found", optarg);
break;
case 'l':
drm = DRM_LAYER2;
break;
case 'o':
oflag = 1;
break;
case 'R':
remote_data = optarg;
remote_datalen = strlen(remote_data);
if (remote_datalen == 0)
fatalx("can't use empty Remote-ID");
break;
case 'v':
daemonize = 0;
debug = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
while (argc > 0) {
parse_destination(argv[0]);
argc--;
argv++;
}
if (interfaces == NULL)
fatalx("no interface given");
if (TAILQ_EMPTY(&svlist))
fatalx("no destination selected");
relay6_setup();
bootp_packet_handler = relay6;
tzset();
time(&cur_time);
if ((pw = getpwnam(DHCRELAY6_USER)) == NULL)
fatalx("user \"%s\" not found", DHCRELAY6_USER);
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("can't drop privileges");
if (daemonize) {
if (daemon(0, 0) == -1)
fatal("daemon");
log_init(0, LOG_DAEMON);
}
log_setverbose(debug);
if (pledge("stdio inet route", NULL) == -1)
fatal("pledge");
dispatch();
exit(0);
}
int
s6fromaddr(struct sockaddr_in6 *sin6, const char *addr, const char *serv,
int passive)
{
struct sockaddr_in6 *sin6p;
struct addrinfo *aip;
struct addrinfo ai;
int rv;
memset(&ai, 0, sizeof(ai));
ai.ai_family = PF_INET6;
ai.ai_socktype = SOCK_DGRAM;
ai.ai_protocol = IPPROTO_UDP;
ai.ai_flags = (passive) ? AI_PASSIVE : 0;
if ((rv = getaddrinfo(addr, serv, &ai, &aip)) != 0) {
log_debug("getaddrinfo: %s", gai_strerror(rv));
return -1;
}
sin6p = (struct sockaddr_in6 *)aip->ai_addr;
*sin6 = *sin6p;
freeaddrinfo(aip);
return 0;
}
void
relay6_setup(void)
{
struct interface_info *intf;
struct server_list *sp;
int flag = 1;
struct sockaddr_in6 sin6;
struct ipv6_mreq mreq6;
TAILQ_FOREACH(sp, &svlist, entry) {
if (sp->intf == NULL)
continue;
if (sp->intf->dead)
fatalx("interface '%s' is down", sp->intf->name);
}
if (drm == DRM_LAYER2) {
TAILQ_FOREACH(sp, &svlist, entry) {
sp->intf = register_interface(sp->intf->name,
got_one);
if (sp->intf == NULL)
fatalx("destination interface "
"registration failed");
}
interfaces = register_interface(interfaces->name, got_one);
if (interfaces == NULL)
fatalx("input interface not configured");
return;
}
TAILQ_FOREACH(sp, &svlist, entry) {
if (!sp->intf->ipv6)
fatalx("%s: no IPv6 address configured",
sp->intf->name);
if (sp->siteglobaladdr && !sp->intf->gipv6)
fatalx("%s: no IPv6 site/global address configured",
sp->intf->name);
}
if (!interfaces->ipv6)
fatalx("%s: no IPv6 address configured", interfaces->name);
clientsd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (clientsd == -1)
fatal("socket");
if (setsockopt(clientsd, SOL_SOCKET, SO_REUSEPORT, &flag,
sizeof(flag)) == -1)
fatal("setsockopt(SO_REUSEPORT)");
if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1)
fatalx("s6fromaddr");
if (bind(clientsd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
fatal("bind");
if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag,
sizeof(flag)) == -1)
fatal("setsockopt(IPV6_RECVPKTINFO)");
memset(&mreq6, 0, sizeof(mreq6));
if (s6fromaddr(&sin6, DHCP6_ADDR_RELAYSERVER, NULL, 0) == -1)
fatalx("s6fromaddr");
memcpy(&mreq6.ipv6mr_multiaddr, &sin6.sin6_addr,
sizeof(mreq6.ipv6mr_multiaddr));
TAILQ_FOREACH(intf, &intflist, entry) {
if (!intf->ipv6)
continue;
mreq6.ipv6mr_interface = intf->index;
if (setsockopt(clientsd, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq6, sizeof(mreq6)) == -1)
fatal("setsockopt(IPV6_JOIN_GROUP)");
}
add_protocol("clientsd", clientsd, mcast6_recv, &clientsd);
serversd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (serversd == -1)
fatal("socket");
if (setsockopt(serversd, SOL_SOCKET, SO_REUSEPORT, &flag,
sizeof(flag)) == -1)
fatal("setsockopt(SO_REUSEPORT)");
if (s6fromaddr(&sin6, NULL, DHCP6_SERVER_PORT_STR, 1) == -1)
fatalx("s6fromaddr");
if (bind(serversd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
fatal("bind");
if (setsockopt(serversd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &flag,
sizeof(flag)) == -1)
fatal("setsockopt(IPV6_RECVPKTINFO)");
add_protocol("serversd", serversd, mcast6_recv, &serversd);
}
char *
print_hw_addr(int htype, int hlen, unsigned char *data)
{
static char habuf[49];
char *s = habuf;
int i, j, slen = sizeof(habuf);
if (htype == 0 || hlen == 0) {
bad:
strlcpy(habuf, "<null>", sizeof habuf);
return habuf;
}
for (i = 0; i < hlen; i++) {
j = snprintf(s, slen, "%02x", data[i]);
if (j <= 0 || j >= slen)
goto bad;
j = strlen (s);
s += j;
slen -= (j + 1);
*s++ = ':';
}
*--s = '\0';
return habuf;
}
const char *
v6addr2str(struct in6_addr *addr)
{
static int bufpos = 0;
static char buf[3][256];
bufpos = (bufpos + 1) % 3;
buf[bufpos][0] = '[';
if (inet_ntop(AF_INET6, addr, &buf[bufpos][1],
sizeof(buf[bufpos])) == NULL)
return "[unknown]";
strlcat(buf[bufpos], "]", sizeof(buf[bufpos]));
return buf[bufpos];
}
const char *
dhcp6type2str(uint8_t msgtype)
{
switch (msgtype) {
case DHCP6_MT_REQUEST:
return "REQUEST";
case DHCP6_MT_RENEW:
return "RENEW";
case DHCP6_MT_REBIND:
return "REBIND";
case DHCP6_MT_RELEASE:
return "RELEASE";
case DHCP6_MT_DECLINE:
return "DECLINE";
case DHCP6_MT_INFORMATIONREQUEST:
return "INFORMATION-REQUEST";
case DHCP6_MT_SOLICIT:
return "SOLICIT";
case DHCP6_MT_ADVERTISE:
return "ADVERTISE";
case DHCP6_MT_CONFIRM:
return "CONFIRM";
case DHCP6_MT_REPLY:
return "REPLY";
case DHCP6_MT_RECONFIGURE:
return "RECONFIGURE";
case DHCP6_MT_RELAYREPL:
return "RELAY-REPLY";
case DHCP6_MT_RELAYFORW:
return "RELAY-FORWARD";
default:
return "UNKNOWN";
}
}
int
relay6_pushrelaymsg(struct packet_ctx *pc, struct interface_info *intf,
uint8_t *p, size_t *plen, size_t ptotal)
{
struct dhcp6_relay_packet *dsr;
struct dhcp6_option *dso;
size_t rmlen, dhcplen, optoff;
size_t railen, remotelen;
if (pc->pc_raidata != NULL)
railen = sizeof(*dso) + pc->pc_raidatalen;
else
railen = 0;
if (pc->pc_remote)
remotelen = sizeof(*dso) + ENTERPRISENO_LEN +
pc->pc_remotelen;
else
remotelen = 0;
dhcplen = sizeof(*dsr) + railen + remotelen + sizeof(*dso) + *plen;
rmlen = sizeof(struct ether_header) + sizeof(struct ip6_hdr) +
sizeof(struct udphdr) + dhcplen;
if (rmlen > ptotal) {
log_info("Relay message too big");
return -1;
}
optoff = sizeof(*dsr) + railen + remotelen + sizeof(*dso);
memmove(p + optoff, p, *plen);
dsr = (struct dhcp6_relay_packet *)p;
dsr->dsr_msgtype = DHCP6_MT_RELAYFORW;
if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr,
&in6alldhcprelay, sizeof(in6alldhcprelay)) == 0)
dsr->dsr_hopcount = 0;
else
dsr->dsr_hopcount = DHCP6_HOP_LIMIT;
dsr->dsr_linkaddr = intf->linklocal;
memcpy(&dsr->dsr_peer, &ss2sin6(&pc->pc_src)->sin6_addr,
sizeof(dsr->dsr_peer));
if (railen > 0) {
dso = dsr->dsr_options;
dso->dso_code = htons(DHCP6_OPT_INTERFACEID);
dso->dso_length = htons(pc->pc_raidatalen);
memcpy(dso->dso_data, pc->pc_raidata, pc->pc_raidatalen);
}
if (remotelen > 0) {
dso = (struct dhcp6_option *)
((uint8_t *)dsr->dsr_options + railen);
dso->dso_code = htons(DHCP6_OPT_REMOTEID);
dso->dso_length =
htons(sizeof(pc->pc_enterpriseno) + pc->pc_remotelen);
memcpy(dso->dso_data, &pc->pc_enterpriseno,
sizeof(pc->pc_enterpriseno));
memcpy(dso->dso_data + sizeof(pc->pc_enterpriseno),
pc->pc_remote, pc->pc_remotelen);
}
dso = (struct dhcp6_option *)
((uint8_t *)dsr->dsr_options + railen + remotelen);
dso->dso_code = htons(DHCP6_OPT_RELAY_MSG);
dso->dso_length = htons(*plen);
*plen = dhcplen;
return 0;
}
int
relay6_poprelaymsg(struct packet_ctx *pc, struct interface_info **intf,
uint8_t *p, size_t *plen)
{
struct dhcp6_relay_packet *dsr = (struct dhcp6_relay_packet *)p;
struct dhcp6_packet *ds = NULL;
struct dhcp6_option *dso;
struct in6_addr linkaddr;
size_t pleft = *plen, ifnamelen = 0;
size_t dsolen, dhcplen = 0;
uint16_t optcode;
char ifname[64];
*intf = NULL;
if (dsr->dsr_msgtype != DHCP6_MT_RELAYREPL) {
log_debug("Invalid relay-message (%s) to pop",
dhcp6type2str(dsr->dsr_msgtype));
return -1;
}
ss2sin6(&pc->pc_dst)->sin6_addr = dsr->dsr_peer;
linkaddr = dsr->dsr_linkaddr;
dso = dsr->dsr_options;
pleft -= sizeof(*dsr);
while (pleft > sizeof(*dso)) {
optcode = ntohs(dso->dso_code);
dsolen = sizeof(*dso) + ntohs(dso->dso_length);
if (dsolen > pleft) {
log_debug("invalid packet: payload greater than "
"packet content (%ld, bytes left %ld)",
dsolen, pleft);
return -1;
}
if (optcode == DHCP6_OPT_INTERFACEID) {
ifnamelen = dsolen - sizeof(*dso);
if (ifnamelen >= sizeof(ifname)) {
log_info("received interface id with "
"truncated interface name");
ifnamelen = sizeof(ifname) - 1;
}
memcpy(ifname, dso->dso_data, ifnamelen);
ifname[ifnamelen] = 0;
dso = (struct dhcp6_option *)
((uint8_t *)dso + dsolen);
pleft -= dsolen;
continue;
}
if (optcode != DHCP6_OPT_RELAY_MSG) {
log_debug("ignoring option type %d", optcode);
dso = (struct dhcp6_option *)
((uint8_t *)dso + dsolen);
pleft -= dsolen;
continue;
}
ds = (struct dhcp6_packet *)dso->dso_data;
dhcplen = ntohs(dso->dso_length);
dso = (struct dhcp6_option *)((uint8_t *)dso + dsolen);
pleft -= dsolen;
}
if (ds == NULL || dhcplen == 0) {
log_debug("Could not find relay-message option");
return -1;
}
memmove(p, ds, dhcplen);
*plen = dhcplen;
ds = (struct dhcp6_packet *)p;
if (ds->ds_msgtype != DHCP6_MT_RELAYREPL)
ss2sin6(&pc->pc_dst)->sin6_port =
htons(DHCP6_CLIENT_PORT);
if (ifnamelen == 0)
goto use_linkaddr;
if ((*intf = iflist_getbyname(ifname)) == NULL) {
log_debug(" Interface-ID found, but no interface matches.");
*intf = interfaces;
}
use_linkaddr:
if (memcmp(&linkaddr, &in6addr_any, sizeof(linkaddr)) != 0) {
if ((*intf = iflist_getbyaddr6(&linkaddr)) != NULL)
return 0;
log_debug("Could not find interface using "
"address %s", v6addr2str(&linkaddr));
}
return 0;
}
void
rai_configure(struct packet_ctx *pc, struct interface_info *intf)
{
if (remote_data != NULL) {
pc->pc_remote = remote_data;
pc->pc_remotelen = remote_datalen;
pc->pc_enterpriseno = htonl(enterpriseno);
}
if (drm == DRM_LAYER2)
goto select_rai;
if (oflag == 0)
return;
select_rai:
if (rai_data == NULL) {
pc->pc_raidata = intf->name;
pc->pc_raidatalen = strlen(intf->name);
} else {
pc->pc_raidata = rai_data;
pc->pc_raidatalen = rai_datalen;
}
}
void
relay6_logsrcaddr(struct packet_ctx *pc, struct interface_info *intf,
uint8_t msgtype)
{
const char *type;
type = (msgtype == DHCP6_MT_RELAYREPL) ? "reply" : "forward";
if (drm == DRM_LAYER2)
log_info("forwarded relay-%s for %s to %s",
type, print_hw_addr(pc->pc_htype, pc->pc_hlen,
pc->pc_smac), intf->name);
else
log_info("forwarded relay-%s for %s to %s%%%s",
type,
v6addr2str(&ss2sin6(&pc->pc_srcorig)->sin6_addr),
v6addr2str(&ss2sin6(&pc->pc_dst)->sin6_addr),
intf->name);
}
void
relay6(struct interface_info *intf, void *p, size_t plen,
struct packet_ctx *pc)
{
struct dhcp6_packet *ds = (struct dhcp6_packet *)p;
struct dhcp6_relay_packet *dsr = (struct dhcp6_relay_packet *)p;
struct interface_info *dstif = NULL;
struct server_list *sp;
size_t buflen = plen;
int clientdir = (intf != interfaces);
uint8_t msgtype, hopcount = 0;
if (plen < (int)sizeof(*ds)) {
log_debug("invalid packet size");
return;
}
rai_configure(pc, intf);
msgtype = ds->ds_msgtype;
log_debug("%s: received %s from %s",
intf->name, dhcp6type2str(msgtype),
v6addr2str(&ss2sin6(&pc->pc_src)->sin6_addr));
switch (msgtype) {
case DHCP6_MT_ADVERTISE:
case DHCP6_MT_REPLY:
case DHCP6_MT_RECONFIGURE:
if (clientdir == 0) {
log_debug(" dropped reply in opposite direction");
return;
}
case DHCP6_MT_REQUEST:
case DHCP6_MT_RENEW:
case DHCP6_MT_REBIND:
case DHCP6_MT_RELEASE:
case DHCP6_MT_DECLINE:
case DHCP6_MT_INFORMATIONREQUEST:
case DHCP6_MT_SOLICIT:
case DHCP6_MT_CONFIRM:
if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p,
&buflen, DHCP_MTU_MAX) == -1) {
log_debug(" message encapsulation failed");
return;
}
break;
case DHCP6_MT_RELAYREPL:
if (clientdir == 0) {
log_debug(" dropped reply in opposite direction");
return;
}
if (relay6_poprelaymsg(pc, &dstif, (uint8_t *)p,
&buflen) == -1) {
log_debug(" failed to pop relay-message");
return;
}
pc->pc_sd = clientsd;
break;
case DHCP6_MT_RELAYFORW:
if (memcmp(&ss2sin6(&pc->pc_dst)->sin6_addr,
&in6alldhcprelay, sizeof(in6alldhcprelay)) != 0) {
log_debug(" wrong destination");
return;
}
hopcount = dsr->dsr_hopcount + 1;
if (hopcount >= DHCP6_HOP_LIMIT) {
log_debug(" hop limit reached");
return;
}
if (relay6_pushrelaymsg(pc, intf, (uint8_t *)p,
&buflen, DHCP_MTU_MAX) == -1) {
log_debug(" failed to push relay message");
return;
}
dsr = (struct dhcp6_relay_packet *)p;
dsr->dsr_msgtype = msgtype;
dsr->dsr_peer = ss2sin6(&pc->pc_src)->sin6_addr;
dsr->dsr_hopcount = hopcount;
break;
default:
log_debug(" unknown message type %d", ds->ds_msgtype);
return;
}
if (dstif != NULL) {
relay6_logsrcaddr(pc, dstif, msgtype);
send_packet(dstif, p, buflen, pc);
return;
}
if (clientdir) {
relay6_logsrcaddr(pc, interfaces, msgtype);
send_packet(interfaces, p, buflen, pc);
return;
}
TAILQ_FOREACH(sp, &svlist, entry) {
if (sp->intf == intf &&
sp->to.ss_family == 0)
continue;
if (msgtype != DHCP6_MT_REPLY &&
sp->to.ss_family == AF_INET6)
pc->pc_dst = sp->to;
relay6_logsrcaddr(pc, sp->intf, msgtype);
send_packet(sp->intf, p, buflen, pc);
}
}
void
mcast6_recv(struct protocol *l)
{
struct in6_pktinfo *ipi6 = NULL;
struct cmsghdr *cmsg;
struct interface_info *intf;
int sd = *(int *)l->local;
ssize_t recvlen;
struct packet_ctx pc;
struct msghdr msg;
struct sockaddr_storage ss;
struct iovec iov[2];
uint8_t iovbuf[4096];
uint8_t cmsgbuf[
CMSG_SPACE(sizeof(struct in6_pktinfo))
];
memset(&pc, 0, sizeof(pc));
iov[0].iov_base = iovbuf;
iov[0].iov_len = sizeof(iovbuf);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
msg.msg_name = &ss;
msg.msg_namelen = sizeof(ss);
if ((recvlen = recvmsg(sd, &msg, 0)) == -1) {
log_warn("%s: recvmsg failed", __func__);
return;
}
if (ss.ss_family != AF_INET6) {
log_debug("received non IPv6 packet");
return;
}
if (iflist_getbyaddr6(&ss2sin6(&ss)->sin6_addr) != NULL)
return;
pc.pc_srcorig = pc.pc_src = ss;
ss2sin6(&pc.pc_dst)->sin6_family = AF_INET6;
ss2sin6(&pc.pc_dst)->sin6_len = sizeof(struct sockaddr_in6);
ss2sin6(&pc.pc_dst)->sin6_addr = in6alldhcprelay;
ss2sin6(&pc.pc_dst)->sin6_port = htons(DHCP6_SERVER_PORT);
pc.pc_sd = serversd;
for (cmsg = (struct cmsghdr *)CMSG_FIRSTHDR(&msg); cmsg;
cmsg = (struct cmsghdr *)CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level != IPPROTO_IPV6)
continue;
switch (cmsg->cmsg_type) {
case IPV6_PKTINFO:
ipi6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
break;
}
}
if (ipi6 == NULL) {
log_debug("failed to get packet interface");
return;
}
intf = iflist_getbyindex(ipi6->ipi6_ifindex);
if (intf == NULL) {
log_debug("failed to find packet interface: %u",
ipi6->ipi6_ifindex);
return;
}
if (bootp_packet_handler)
(*bootp_packet_handler)(intf, iovbuf, recvlen, &pc);
}