233 lines
7.7 KiB
C
233 lines
7.7 KiB
C
/*
|
|
* Copyright (c) 2016-2024 OARC, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "bpft.h"
|
|
#include "iaddr.h"
|
|
|
|
#include <ldns/ldns.h>
|
|
|
|
void prepare_bpft(void)
|
|
{
|
|
unsigned udp10_mbs, udp10_mbc, udp11_mbc; // udp11_mbs
|
|
text_list bpfl;
|
|
text_ptr text;
|
|
size_t len;
|
|
char* p;
|
|
|
|
/* Prepare the must-be-set and must-be-clear tests. */
|
|
udp10_mbs = udp10_mbc = udp11_mbc = 0U; // udp11_mbs
|
|
if ((dir_wanted & DIR_INITIATE) != 0) {
|
|
if ((dir_wanted & DIR_RESPONSE) == 0)
|
|
udp10_mbc |= UDP10_QR_MASK;
|
|
} else if ((dir_wanted & DIR_RESPONSE) != 0) {
|
|
udp10_mbs |= UDP10_QR_MASK;
|
|
}
|
|
if ((msg_wanted & MSG_UPDATE) != 0) {
|
|
if ((msg_wanted & (MSG_QUERY | MSG_NOTIFY)) == 0)
|
|
udp10_mbs |= (LDNS_PACKET_UPDATE << UDP10_OP_SHIFT);
|
|
} else if ((msg_wanted & MSG_NOTIFY) != 0) {
|
|
if ((msg_wanted & (MSG_QUERY | MSG_UPDATE)) == 0)
|
|
udp10_mbs |= (LDNS_PACKET_NOTIFY << UDP10_OP_SHIFT);
|
|
} else if ((msg_wanted & MSG_QUERY) != 0) {
|
|
udp10_mbc |= UDP10_OP_MASK;
|
|
}
|
|
if (err_wanted == ERR_NO) {
|
|
udp10_mbc |= UDP10_TC_MASK;
|
|
udp11_mbc |= UDP11_RC_MASK;
|
|
}
|
|
|
|
/*
|
|
* Model
|
|
* (vlan) and (transport)
|
|
* (vlan) and ((icmp) or (frags) or (dns))
|
|
* (vlan) and ((icmp) or (frags) or ((ports) and (hosts)))
|
|
* (vlan) and ((icmp) or (frags) or (((tcp) or (udp)) and (hosts)))
|
|
* [(vlan) and] ( [(icmp) or] [(frags) or] ( ( [(tcp) or] (udp) ) [and (hosts)] ) )
|
|
*/
|
|
|
|
/* Make a BPF program to do early course kernel-level filtering. */
|
|
INIT_LIST(bpfl);
|
|
len = 0;
|
|
if (!EMPTY(vlans_excl))
|
|
len += text_add(&bpfl, "vlan and ("); /* vlan and ( transports ... */
|
|
else
|
|
len += text_add(&bpfl, "("); /* ( transports ... */
|
|
if (wanticmp) {
|
|
len += text_add(&bpfl, " ( icmp or icmp6 ) or");
|
|
}
|
|
if (wantfrags) {
|
|
len += text_add(&bpfl, " ( ip[6:2] & 0x1fff != 0 or ip6[6] = 44 ) or");
|
|
}
|
|
len += text_add(&bpfl, " ("); /* ( dns ... */
|
|
len += text_add(&bpfl, " ("); /* ( ports ... */
|
|
if (wanttcp) {
|
|
len += text_add(&bpfl, " ( tcp port %d ) or", dns_port);
|
|
/* tcp packets can be filtered by initiators/responders, but
|
|
* not mbs/mbc. */
|
|
}
|
|
len += text_add(&bpfl, " ( udp port %d and ( ip6 or ( ip", dns_port);
|
|
|
|
if (udp10_mbc != 0)
|
|
len += text_add(&bpfl, " and udp[10] & 0x%x = 0",
|
|
udp10_mbc);
|
|
if (udp10_mbs != 0)
|
|
len += text_add(&bpfl, " and udp[10] & 0x%x = 0x%x",
|
|
udp10_mbs, udp10_mbs);
|
|
if (udp11_mbc != 0)
|
|
len += text_add(&bpfl, " and udp[11] & 0x%x = 0",
|
|
udp11_mbc);
|
|
/* Dead code, udp11_mbs never set
|
|
if (udp11_mbs != 0)
|
|
len += text_add(&bpfl, " and udp[11] & 0x%x = 0x%x",
|
|
udp11_mbs, udp11_mbs);
|
|
*/
|
|
|
|
if (err_wanted != ERR_NO) {
|
|
len += text_add(&bpfl, " and (");
|
|
if ((err_wanted & ERR_TRUNC) != 0) {
|
|
len += text_add(&bpfl, " udp[10] & 0x%x = 0x%x or", UDP10_TC_MASK, UDP10_TC_MASK);
|
|
}
|
|
len += text_add(&bpfl, " 0x%x << (udp[11] & 0xf) & 0x%x != 0 )", ERR_RCODE_BASE, err_wanted);
|
|
}
|
|
|
|
len += text_add(&bpfl, " )))"); /* ... udp 53 ) */
|
|
len += text_add(&bpfl, " )"); /* ... ports ) */
|
|
if (options.bpf_hosts_apply_all) {
|
|
len += text_add(&bpfl, " )"); /* ... dns ) */
|
|
len += text_add(&bpfl, " )"); /* ... transport ) */
|
|
}
|
|
if (!EMPTY(initiators) || !EMPTY(responders)) {
|
|
const char* or = "or", *lp = "(", *sep;
|
|
endpoint_ptr ep;
|
|
|
|
len += text_add(&bpfl, " and host");
|
|
sep = lp;
|
|
for (ep = HEAD(initiators);
|
|
ep != NULL;
|
|
ep = NEXT(ep, link)) {
|
|
len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
|
|
sep = or ;
|
|
}
|
|
for (ep = HEAD(responders);
|
|
ep != NULL;
|
|
ep = NEXT(ep, link)) {
|
|
len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
|
|
sep = or ;
|
|
}
|
|
len += text_add(&bpfl, " )");
|
|
}
|
|
if (!EMPTY(not_initiators) || !EMPTY(not_responders)) {
|
|
const char* or = "or", *lp = "(", *sep;
|
|
endpoint_ptr ep;
|
|
|
|
len += text_add(&bpfl, " and not host");
|
|
sep = lp;
|
|
for (ep = HEAD(not_initiators);
|
|
ep != NULL;
|
|
ep = NEXT(ep, link)) {
|
|
len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
|
|
sep = or ;
|
|
}
|
|
for (ep = HEAD(not_responders);
|
|
ep != NULL;
|
|
ep = NEXT(ep, link)) {
|
|
len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
|
|
sep = or ;
|
|
}
|
|
len += text_add(&bpfl, " )");
|
|
}
|
|
if (!options.bpf_hosts_apply_all) {
|
|
len += text_add(&bpfl, " )"); /* ... dns ) */
|
|
len += text_add(&bpfl, " )"); /* ... transport ) */
|
|
}
|
|
if (extra_bpf)
|
|
len += text_add(&bpfl, " and ( %s )", extra_bpf);
|
|
|
|
bpft = calloc(len + 1, sizeof(char));
|
|
assert(bpft != NULL);
|
|
p = bpft;
|
|
for (text = HEAD(bpfl); text != NULL; text = NEXT(text, link)) {
|
|
memcpy(p, text->text, text->len);
|
|
p += text->len;
|
|
}
|
|
text_free(&bpfl);
|
|
if (!EMPTY(vlans_incl)) {
|
|
char* bpft_vlan;
|
|
|
|
len = (2 * len) + 64; /* add enough for the extra in snprintf() below */
|
|
bpft_vlan = calloc(len, sizeof(char));
|
|
assert(bpft_vlan != NULL);
|
|
|
|
snprintf(bpft_vlan, len, "( %s ) or ( vlan and ( %s ) )", bpft, bpft);
|
|
free(bpft);
|
|
bpft = bpft_vlan;
|
|
}
|
|
if (dumptrace >= 1)
|
|
fprintf(stderr, "%s: \"%s\"\n", ProgramName, bpft);
|
|
}
|
|
|
|
size_t text_add(text_list* list, const char* fmt, ...)
|
|
{
|
|
text_ptr text;
|
|
va_list ap;
|
|
int len;
|
|
|
|
text = calloc(1, sizeof *text);
|
|
assert(text != NULL);
|
|
INIT_LINK(text, link);
|
|
va_start(ap, fmt);
|
|
len = vasprintf(&text->text, fmt, ap);
|
|
assert(len >= 0);
|
|
va_end(ap);
|
|
text->len = len;
|
|
APPEND(*list, text, link);
|
|
return (text->len);
|
|
}
|
|
|
|
void text_free(text_list* list)
|
|
{
|
|
text_ptr at, text;
|
|
|
|
for (at = HEAD(*list); at;) {
|
|
text = at;
|
|
at = NEXT(text, link);
|
|
|
|
UNLINK(*list, text, link);
|
|
free(text->text);
|
|
assert(text != (void*)-1);
|
|
free(text);
|
|
}
|
|
}
|