1 /*
2  * Copyright (c) 2016-2021, OARC, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in
14  *    the documentation and/or other materials provided with the
15  *    distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "config.h"
36 
37 #include "bpft.h"
38 #include "iaddr.h"
39 
40 #include <ldns/ldns.h>
41 
prepare_bpft(void)42 void prepare_bpft(void)
43 {
44     unsigned  udp10_mbs, udp10_mbc, udp11_mbc; //udp11_mbs
45     text_list bpfl;
46     text_ptr  text;
47     size_t    len;
48     char*     p;
49 
50     /* Prepare the must-be-set and must-be-clear tests. */
51     udp10_mbs = udp10_mbc = udp11_mbc = 0U; // udp11_mbs
52     if ((dir_wanted & DIR_INITIATE) != 0) {
53         if ((dir_wanted & DIR_RESPONSE) == 0)
54             udp10_mbc |= UDP10_QR_MASK;
55     } else if ((dir_wanted & DIR_RESPONSE) != 0) {
56         udp10_mbs |= UDP10_QR_MASK;
57     }
58     if ((msg_wanted & MSG_UPDATE) != 0) {
59         if ((msg_wanted & (MSG_QUERY | MSG_NOTIFY)) == 0)
60             udp10_mbs |= (LDNS_PACKET_UPDATE << UDP10_OP_SHIFT);
61     } else if ((msg_wanted & MSG_NOTIFY) != 0) {
62         if ((msg_wanted & (MSG_QUERY | MSG_UPDATE)) == 0)
63             udp10_mbs |= (LDNS_PACKET_NOTIFY << UDP10_OP_SHIFT);
64     } else if ((msg_wanted & MSG_QUERY) != 0) {
65         udp10_mbc |= UDP10_OP_MASK;
66     }
67     if (err_wanted == ERR_NO) {
68         udp10_mbc |= UDP10_TC_MASK;
69         udp11_mbc |= UDP11_RC_MASK;
70     }
71 
72     /*
73  * Model
74  * (vlan) and (transport)
75  * (vlan) and ((icmp) or (frags) or (dns))
76  * (vlan) and ((icmp) or (frags) or ((ports) and (hosts)))
77  * (vlan) and ((icmp) or (frags) or (((tcp) or (udp)) and (hosts)))
78  * [(vlan) and] ( [(icmp) or] [(frags) or] ( ( [(tcp) or] (udp) ) [and (hosts)] ) )
79  */
80 
81     /* Make a BPF program to do early course kernel-level filtering. */
82     INIT_LIST(bpfl);
83     len = 0;
84     if (!EMPTY(vlans_excl))
85         len += text_add(&bpfl, "vlan and ("); /* vlan and ( transports ...  */
86     else
87         len += text_add(&bpfl, "("); /* ( transports ...  */
88     if (wanticmp) {
89         len += text_add(&bpfl, " ( ip proto 1 or ip proto 58 ) or");
90     }
91     if (wantfrags) {
92         len += text_add(&bpfl, " ( ip[6:2] & 0x1fff != 0 or ip6[6] = 44 ) or");
93     }
94     len += text_add(&bpfl, " ("); /* ( dns ...  */
95     len += text_add(&bpfl, " ("); /* ( ports ...  */
96     if (wanttcp) {
97         len += text_add(&bpfl, " ( tcp port %d ) or", dns_port);
98         /* tcp packets can be filtered by initiators/responders, but
99          * not mbs/mbc. */
100     }
101     len += text_add(&bpfl, " ( udp port %d and ( ip6 or ( ip", dns_port);
102 
103     if (udp10_mbc != 0)
104         len += text_add(&bpfl, " and udp[10] & 0x%x = 0",
105             udp10_mbc);
106     if (udp10_mbs != 0)
107         len += text_add(&bpfl, " and udp[10] & 0x%x = 0x%x",
108             udp10_mbs, udp10_mbs);
109     if (udp11_mbc != 0)
110         len += text_add(&bpfl, " and udp[11] & 0x%x = 0",
111             udp11_mbc);
112     /* Dead code, udp11_mbs never set
113         if (udp11_mbs != 0)
114             len += text_add(&bpfl, " and udp[11] & 0x%x = 0x%x",
115                     udp11_mbs, udp11_mbs);
116 */
117 
118     if (err_wanted != ERR_NO) {
119         len += text_add(&bpfl, " and (");
120         if ((err_wanted & ERR_TRUNC) != 0) {
121             len += text_add(&bpfl, " udp[10] & 0x%x = 0x%x or", UDP10_TC_MASK, UDP10_TC_MASK);
122         }
123         len += text_add(&bpfl, " 0x%x << (udp[11] & 0xf) & 0x%x != 0 )", ERR_RCODE_BASE, err_wanted);
124     }
125 
126     len += text_add(&bpfl, " )))"); /*  ... udp 53 ) */
127     len += text_add(&bpfl, " )"); /*  ... ports ) */
128     if (options.bpf_hosts_apply_all) {
129         len += text_add(&bpfl, " )"); /*  ... dns ) */
130         len += text_add(&bpfl, " )"); /* ... transport ) */
131     }
132     if (!EMPTY(initiators) || !EMPTY(responders)) {
133         const char* or = "or", *lp = "(", *sep;
134         endpoint_ptr ep;
135 
136         len += text_add(&bpfl, " and host");
137         sep = lp;
138         for (ep = HEAD(initiators);
139              ep != NULL;
140              ep = NEXT(ep, link)) {
141             len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
142             sep = or ;
143         }
144         for (ep = HEAD(responders);
145              ep != NULL;
146              ep = NEXT(ep, link)) {
147             len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
148             sep = or ;
149         }
150         len += text_add(&bpfl, " )");
151     }
152     if (!EMPTY(not_initiators) || !EMPTY(not_responders)) {
153         const char* or = "or", *lp = "(", *sep;
154         endpoint_ptr ep;
155 
156         len += text_add(&bpfl, " and not host");
157         sep = lp;
158         for (ep = HEAD(not_initiators);
159              ep != NULL;
160              ep = NEXT(ep, link)) {
161             len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
162             sep = or ;
163         }
164         for (ep = HEAD(not_responders);
165              ep != NULL;
166              ep = NEXT(ep, link)) {
167             len += text_add(&bpfl, " %s %s", sep, ia_str(ep->ia));
168             sep = or ;
169         }
170         len += text_add(&bpfl, " )");
171     }
172     if (!options.bpf_hosts_apply_all) {
173         len += text_add(&bpfl, " )"); /*  ... dns ) */
174         len += text_add(&bpfl, " )"); /* ... transport ) */
175     }
176     if (extra_bpf)
177         len += text_add(&bpfl, " and ( %s )", extra_bpf);
178 
179     bpft = calloc(len + 1, sizeof(char));
180     assert(bpft != NULL);
181     p = bpft;
182     for (text = HEAD(bpfl); text != NULL; text = NEXT(text, link)) {
183         memcpy(p, text->text, text->len);
184         p += text->len;
185     }
186     text_free(&bpfl);
187     if (!EMPTY(vlans_incl)) {
188         char* bpft_vlan;
189 
190         len       = (2 * len) + 64; /* add enough for the extra in snprintf() below */
191         bpft_vlan = calloc(len, sizeof(char));
192         assert(bpft_vlan != NULL);
193 
194         snprintf(bpft_vlan, len, "( %s ) or ( vlan and ( %s ) )", bpft, bpft);
195         free(bpft);
196         bpft = bpft_vlan;
197     }
198     if (dumptrace >= 1)
199         fprintf(stderr, "%s: \"%s\"\n", ProgramName, bpft);
200 }
201 
text_add(text_list * list,const char * fmt,...)202 size_t text_add(text_list* list, const char* fmt, ...)
203 {
204     text_ptr text;
205     va_list  ap;
206     int      len;
207 
208     text = calloc(1, sizeof *text);
209     assert(text != NULL);
210     INIT_LINK(text, link);
211     va_start(ap, fmt);
212     len = vasprintf(&text->text, fmt, ap);
213     assert(len >= 0);
214     va_end(ap);
215     text->len = len;
216     APPEND(*list, text, link);
217     return (text->len);
218 }
219 
text_free(text_list * list)220 void text_free(text_list* list)
221 {
222     text_ptr at, text;
223 
224     for (at = HEAD(*list); at;) {
225         text = at;
226         at   = NEXT(text, link);
227 
228         UNLINK(*list, text, link);
229         free(text->text);
230         assert(text != (void*)-1);
231         free(text);
232     }
233 }
234