Skip to content

Commit d0e1bb3

Browse files
committed
Packet Filter Linux ignore VLAN tagged packets
If you define kea dhcp on an untagged interface that also has a tagged vlan interface on top, it will see both dhcp packets (eth0 and eth0.10). Linux has a feature for auxiliary data on raw sockets which contains data about the vlan of the packet received. Kea should only answer packets without a vlan in the aux data, since those are the data send to the attached interface. vlan data are missing on the intended interface eth0.10 but set on eth0 for the same packet. There was a discussion about this here: http://kea-users.7364.n8.nabble.com/Kea-users-KEA-DHCP-and-VLANS-td1618.html Signed-off-by: Sven Auhagen <[email protected]>
1 parent 8a575ad commit d0e1bb3

File tree

1 file changed

+42
-2
lines changed

1 file changed

+42
-2
lines changed

src/lib/dhcp/pkt_filter_lpf.cc

+42-2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,15 @@ PktFilterLPF::openSocket(Iface& iface,
180180
<< " on the socket " << sock);
181181
}
182182

183+
int one = 1;
184+
if(setsockopt(sock, SOL_PACKET, PACKET_AUXDATA, &one, sizeof(one)) < 0)
185+
{
186+
close(sock);
187+
close(fallback);
188+
isc_throw(SocketConfigError, "Failed to install packet aux data"
189+
<< " on the socket " << sock);
190+
}
191+
183192
struct sockaddr_ll sa;
184193
memset(&sa, 0, sizeof(sockaddr_ll));
185194
sa.sll_family = AF_PACKET;
@@ -216,6 +225,20 @@ PktFilterLPF::openSocket(Iface& iface,
216225
Pkt4Ptr
217226
PktFilterLPF::receive(Iface& iface, const SocketInfo& socket_info) {
218227
uint8_t raw_buf[IfaceMgr::RCVBUFSIZE];
228+
unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
229+
struct iovec iov;
230+
struct msghdr msg;
231+
struct cmsghdr *cmsg;
232+
uint16_t tp_vlan_tpid;
233+
234+
iov.iov_base = &raw_buf;
235+
iov.iov_len = sizeof(raw_buf);
236+
237+
memset(&msg, 0, sizeof(msg));
238+
msg.msg_iov = &iov;
239+
msg.msg_iovlen = 1;
240+
msg.msg_control = cmsgbuf;
241+
msg.msg_controllen = sizeof(cmsgbuf);
219242
// First let's get some data from the fallback socket. The data will be
220243
// discarded but we don't want the socket buffer to bloat. We get the
221244
// packets from the socket in loop but most of the time the loop will
@@ -237,8 +260,11 @@ PktFilterLPF::receive(Iface& iface, const SocketInfo& socket_info) {
237260

238261
// Now that we finished getting data from the fallback socket, we
239262
// have to get the data from the raw socket too.
240-
int data_len = read(socket_info.sockfd_, raw_buf, sizeof(raw_buf));
241-
// If negative value is returned by read(), it indicates that an
263+
int data_len;
264+
do {
265+
data_len = recvmsg(socket_info.sockfd_, &msg, 0);
266+
} while (data_len < 0 && errno == EINTR);
267+
// If negative value is returned by recvmsg(), it indicates that an
242268
// error occurred. If returned value is 0, no data was read from the
243269
// socket. In both cases something has gone wrong, because we expect
244270
// that a chunk of data is there. We signal the lack of data by
@@ -247,6 +273,20 @@ PktFilterLPF::receive(Iface& iface, const SocketInfo& socket_info) {
247273
return Pkt4Ptr();
248274
}
249275

276+
tp_vlan_tpid = 0;
277+
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
278+
if (cmsg->cmsg_level == SOL_PACKET &&
279+
cmsg->cmsg_type == PACKET_AUXDATA) {
280+
struct tpacket_auxdata *aux = (struct tpacket_auxdata *)CMSG_DATA(cmsg);
281+
tp_vlan_tpid = aux->tp_vlan_tpid;
282+
}
283+
}
284+
285+
// Only server non VLAN/QinQ Traffic
286+
if (tp_vlan_tpid != 0) {
287+
return Pkt4Ptr();
288+
}
289+
250290
InputBuffer buf(raw_buf, data_len);
251291

252292
// @todo: This is awkward way to solve the chicken and egg problem

0 commit comments

Comments
 (0)