xref: /freebsd/sys/netpfil/pf/pf_nl.c (revision 8f7ed58a15556bf567ff876e1999e4fe4d684e1d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org>
5  * Copyright (c) 2023 Rubicon Communications, LLC (Netgate)
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/malloc.h>
32 #include <sys/mbuf.h>
33 #include <sys/socket.h>
34 #include <sys/ucred.h>
35 
36 #include <net/pfvar.h>
37 
38 #include <netlink/netlink.h>
39 #include <netlink/netlink_ctl.h>
40 #include <netlink/netlink_generic.h>
41 #include <netlink/netlink_message_writer.h>
42 
43 #include <netpfil/pf/pf_nl.h>
44 
45 #define	DEBUG_MOD_NAME	nl_pf
46 #define	DEBUG_MAX_LEVEL	LOG_DEBUG3
47 #include <netlink/netlink_debug.h>
48 _DECLARE_DEBUG(LOG_DEBUG);
49 
50 struct nl_parsed_state {
51 	uint8_t		version;
52 	uint32_t	id;
53 	uint32_t	creatorid;
54 	char		ifname[IFNAMSIZ];
55 	uint16_t	proto;
56 	sa_family_t	af;
57 	struct pf_addr	addr;
58 	struct pf_addr	mask;
59 };
60 
61 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
62 #define	_OUT(_field)	offsetof(struct nl_parsed_state, _field)
63 static const struct nlattr_parser nla_p_state[] = {
64 	{ .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 },
65 	{ .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 },
66 	{ .type = PF_ST_IFNAME, .arg = (const void *)IFNAMSIZ, .off = _OUT(ifname), .cb = nlattr_get_chara },
67 	{ .type = PF_ST_AF, .off = _OUT(proto), .cb = nlattr_get_uint8 },
68 	{ .type = PF_ST_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint16 },
69 	{ .type = PF_ST_FILTER_ADDR, .off = _OUT(addr), .cb = nlattr_get_in6_addr },
70 	{ .type = PF_ST_FILTER_MASK, .off = _OUT(mask), .cb = nlattr_get_in6_addr },
71 };
72 static const struct nlfield_parser nlf_p_generic[] = {
73 	{ .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 },
74 };
75 #undef _IN
76 #undef _OUT
77 NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state);
78 
79 static void
80 dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af)
81 {
82 	switch (af) {
83 	case AF_INET:
84 		nlattr_add(nw, attr, 4, &addr->v4);
85 		break;
86 	case AF_INET6:
87 		nlattr_add(nw, attr, 16, &addr->v6);
88 		break;
89 	};
90 }
91 
92 static bool
93 dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer)
94 {
95 	int off = nlattr_add_nested(nw, attr);
96 	if (off == 0)
97 		return (false);
98 
99 	nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo);
100 	nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi);
101 	nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff);
102 	nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win);
103 	nlattr_add_u16(nw, PF_STP_MSS, peer->mss);
104 	nlattr_add_u8(nw, PF_STP_STATE, peer->state);
105 	nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale);
106 
107 	if (peer->scrub != NULL) {
108 		struct pf_state_scrub *sc = peer->scrub;
109 		uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP;
110 
111 		nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags);
112 		nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod);
113 		nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl);
114 		nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID);
115 	}
116 	nlattr_set_len(nw, off);
117 
118 	return (true);
119 }
120 
121 static bool
122 dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key)
123 {
124 	int off = nlattr_add_nested(nw, attr);
125 	if (off == 0)
126 		return (false);
127 
128 	dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af);
129 	dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af);
130 	nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]);
131 	nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]);
132 
133 	nlattr_set_len(nw, off);
134 
135 	return (true);
136 }
137 
138 static int
139 dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
140     struct nl_pstate *npt)
141 {
142 	struct nl_writer *nw = npt->nw;
143 	int error = 0;
144 	int af;
145 	struct pf_state_key *key;
146 
147 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
148 		goto enomem;
149 
150 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
151 	ghdr_new->cmd = PFNL_CMD_GETSTATES;
152 	ghdr_new->version = 0;
153 	ghdr_new->reserved = 0;
154 
155 	nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION);
156 
157 	key = s->key[PF_SK_WIRE];
158 	if (!dump_state_key(nw, PF_ST_KEY_WIRE, key))
159 		goto enomem;
160 	key = s->key[PF_SK_STACK];
161 	if (!dump_state_key(nw, PF_ST_KEY_STACK, key))
162 		goto enomem;
163 
164 	af = s->key[PF_SK_WIRE]->af;
165 	nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto);
166 	nlattr_add_u8(nw, PF_ST_AF, af);
167 
168 	nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name);
169 	nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name);
170 	dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af);
171 	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - s->creation);
172 	uint32_t expire = pf_state_expires(s);
173 	if (expire > time_uptime)
174 		expire = expire - time_uptime;
175 	nlattr_add_u32(nw, PF_ST_EXPIRE, expire);
176 	nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction);
177 	nlattr_add_u8(nw, PF_ST_LOG, s->act.log);
178 	nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
179 	nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
180 	uint8_t sync_flags = 0;
181 	if (s->src_node)
182 		sync_flags |= PFSYNC_FLAG_SRCNODE;
183 	if (s->nat_src_node)
184 		sync_flags |= PFSYNC_FLAG_NATSRCNODE;
185 	nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
186 	nlattr_add_u64(nw, PF_ST_ID, s->id);
187 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid));
188 
189 	nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1);
190 	nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1);
191 	nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1);
192 
193 	nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]);
194 	nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]);
195 	nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]);
196 	nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]);
197 	nlattr_add_u32(nw, PF_ST_RTABLEID, s->act.rtableid);
198 	nlattr_add_u8(nw, PF_ST_MIN_TTL, s->act.min_ttl);
199 	nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss);
200 	nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe);
201 	nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe);
202 	nlattr_add_u8(nw, PF_ST_RT, s->rt);
203 	if (s->rt_kif != NULL)
204 		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->rt_kif->pfik_name);
205 
206 	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
207 		goto enomem;
208 	if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst))
209 		goto enomem;
210 
211 	if (nlmsg_end(nw))
212 		return (0);
213 
214 enomem:
215 	error = ENOMEM;
216 	nlmsg_abort(nw);
217 	return (error);
218 }
219 
220 static int
221 handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs,
222     struct nlmsghdr *hdr, struct nl_pstate *npt)
223 {
224 	int error = 0;
225 
226 	hdr->nlmsg_flags |= NLM_F_MULTI;
227 
228 	for (int i = 0; i <= pf_hashmask; i++) {
229 		struct pf_idhash *ih = &V_pf_idhash[i];
230 		struct pf_kstate *s;
231 
232 		if (LIST_EMPTY(&ih->states))
233 			continue;
234 
235 		PF_HASHROW_LOCK(ih);
236 		LIST_FOREACH(s, &ih->states, entry) {
237 			sa_family_t af = s->key[PF_SK_WIRE]->af;
238 
239 			if (s->timeout == PFTM_UNLINKED)
240 				continue;
241 
242 			/* Filter */
243 			if (attrs->creatorid != 0 && s->creatorid != attrs->creatorid)
244 				continue;
245 			if (attrs->ifname[0] != 0 &&
246 			    strncmp(attrs->ifname, s->kif->pfik_name, IFNAMSIZ) != 0)
247 				continue;
248 			if (attrs->proto != 0 && s->key[PF_SK_WIRE]->proto != attrs->proto)
249 				continue;
250 			if (attrs->af != 0 && af != attrs->af)
251 				continue;
252 			if (pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[0],
253 			    &attrs->mask, &attrs->addr, af) &&
254 			    pf_match_addr(1, &s->key[PF_SK_WIRE]->addr[1],
255 			    &attrs->mask, &attrs->addr, af) &&
256 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[0],
257 			    &attrs->mask, &attrs->addr, af) &&
258 			    pf_match_addr(1, &s->key[PF_SK_STACK]->addr[1],
259 			    &attrs->mask, &attrs->addr, af))
260 				continue;
261 
262 			error = dump_state(nlp, hdr, s, npt);
263 			if (error != 0)
264 				break;
265 		}
266 		PF_HASHROW_UNLOCK(ih);
267 	}
268 
269 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
270 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
271 		return (ENOMEM);
272 	}
273 
274 	return (error);
275 }
276 
277 static int
278 handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs,
279     struct nlmsghdr *hdr, struct nl_pstate *npt)
280 {
281 	struct pf_kstate *s = pf_find_state_byid(attrs->id, attrs->creatorid);
282 	if (s == NULL)
283 		return (ENOENT);
284 	return (dump_state(nlp, hdr, s, npt));
285 }
286 
287 static int
288 dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator,
289     struct nl_pstate *npt)
290 {
291 	struct nl_writer *nw = npt->nw;
292 
293 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
294 		goto enomem;
295 
296 	struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
297 	ghdr_new->cmd = PFNL_CMD_GETCREATORS;
298 	ghdr_new->version = 0;
299 	ghdr_new->reserved = 0;
300 
301 	nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator));
302 
303 	if (nlmsg_end(nw))
304 		return (0);
305 
306 enomem:
307 	nlmsg_abort(nw);
308 	return (ENOMEM);
309 }
310 
311 static int
312 pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt)
313 {
314 	int error;
315 
316 	struct nl_parsed_state attrs = {};
317 	error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs);
318 	if (error != 0)
319 		return (error);
320 
321 	if (attrs.id != 0)
322 		error = handle_getstate(npt->nlp, &attrs, hdr, npt);
323 	else
324 		error = handle_dumpstates(npt->nlp, &attrs, hdr, npt);
325 
326 	return (error);
327 }
328 
329 static int
330 pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt)
331 {
332 	uint32_t creators[16];
333 	int error = 0;
334 
335 	bzero(creators, sizeof(creators));
336 
337 	for (int i = 0; i < pf_hashmask; i++) {
338 		struct pf_idhash *ih = &V_pf_idhash[i];
339 		struct pf_kstate *s;
340 
341 		if (LIST_EMPTY(&ih->states))
342 			continue;
343 
344 		PF_HASHROW_LOCK(ih);
345 		LIST_FOREACH(s, &ih->states, entry) {
346 			int j;
347 			if (s->timeout == PFTM_UNLINKED)
348 				continue;
349 
350 			for (j = 0; j < nitems(creators); j++) {
351 				if (creators[j] == s->creatorid)
352 					break;
353 				if (creators[j] == 0) {
354 					creators[j] = s->creatorid;
355 					break;
356 				}
357 			}
358 			if (j == nitems(creators))
359 				printf("Warning: too many creators!\n");
360 		}
361 		PF_HASHROW_UNLOCK(ih);
362 	}
363 
364 	hdr->nlmsg_flags |= NLM_F_MULTI;
365 	for (int i = 0; i < nitems(creators); i++) {
366 		if (creators[i] == 0)
367 			break;
368 		error = dump_creatorid(npt->nlp, hdr, creators[i], npt);
369 	}
370 
371 	if (!nlmsg_end_dump(npt->nw, error, hdr)) {
372 		NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
373 		return (ENOMEM);
374 	}
375 
376 	return (error);
377 }
378 
379 static int
380 pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
381 {
382 	return (pf_start());
383 }
384 
385 static int
386 pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused)
387 {
388 	return (pf_stop());
389 }
390 
391 #define _OUT(_field)	offsetof(struct pf_addr_wrap, _field)
392 static const struct nlattr_parser nla_p_addr_wrap[] = {
393 	{ .type = PF_AT_ADDR, .off = _OUT(v.a.addr), .cb = nlattr_get_in6_addr },
394 	{ .type = PF_AT_MASK, .off = _OUT(v.a.mask), .cb = nlattr_get_in6_addr },
395 	{ .type = PF_AT_IFNAME, .off = _OUT(v.ifname), .arg = (void *)IFNAMSIZ,.cb = nlattr_get_chara },
396 	{ .type = PF_AT_TABLENAME, .off = _OUT(v.tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
397 	{ .type = PF_AT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
398 	{ .type = PF_AT_IFLAGS, .off = _OUT(iflags), .cb = nlattr_get_uint8 },
399 };
400 NL_DECLARE_ATTR_PARSER(addr_wrap_parser, nla_p_addr_wrap);
401 #undef _OUT
402 
403 #define _OUT(_field)	offsetof(struct pf_rule_addr, _field)
404 static const struct nlattr_parser nla_p_ruleaddr[] = {
405 	{ .type = PF_RAT_ADDR, .off = _OUT(addr), .arg = &addr_wrap_parser, .cb = nlattr_get_nested },
406 	{ .type = PF_RAT_SRC_PORT, .off = _OUT(port[0]), .cb = nlattr_get_uint16 },
407 	{ .type = PF_RAT_DST_PORT, .off = _OUT(port[1]), .cb = nlattr_get_uint16 },
408 	{ .type = PF_RAT_NEG, .off = _OUT(neg), .cb = nlattr_get_uint8 },
409 	{ .type = PF_RAT_OP, .off = _OUT(port_op), .cb = nlattr_get_uint8 },
410 };
411 NL_DECLARE_ATTR_PARSER(rule_addr_parser, nla_p_ruleaddr);
412 #undef _OUT
413 
414 #define _OUT(_field)	offsetof(struct pf_mape_portset, _field)
415 static const struct nlattr_parser nla_p_mape_portset[] = {
416 	{ .type = PF_MET_OFFSET, .off = _OUT(offset), .cb = nlattr_get_uint8 },
417 	{ .type = PF_MET_PSID_LEN, .off = _OUT(psidlen), .cb = nlattr_get_uint8 },
418 	{. type = PF_MET_PSID, .off = _OUT(psid), .cb = nlattr_get_uint16 },
419 };
420 NL_DECLARE_ATTR_PARSER(mape_portset_parser, nla_p_mape_portset);
421 #undef _OUT
422 
423 struct nl_parsed_labels
424 {
425 	char		labels[PF_RULE_MAX_LABEL_COUNT][PF_RULE_LABEL_SIZE];
426 	uint32_t	i;
427 };
428 
429 static int
430 nlattr_get_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt,
431     const void *arg, void *target)
432 {
433 	struct nl_parsed_labels *l = (struct nl_parsed_labels *)target;
434 	int ret;
435 
436 	if (l->i >= PF_RULE_MAX_LABEL_COUNT)
437 		return (E2BIG);
438 
439 	ret = nlattr_get_chara(nla, npt, (void *)PF_RULE_LABEL_SIZE,
440 	    l->labels[l->i]);
441 	if (ret == 0)
442 		l->i++;
443 
444 	return (ret);
445 }
446 
447 #define _OUT(_field)	offsetof(struct nl_parsed_labels, _field)
448 static const struct nlattr_parser nla_p_labels[] = {
449 	{ .type = PF_LT_LABEL, .off = 0, .cb = nlattr_get_pf_rule_labels },
450 };
451 NL_DECLARE_ATTR_PARSER(rule_labels_parser, nla_p_labels);
452 #undef _OUT
453 
454 static int
455 nlattr_get_nested_pf_rule_labels(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
456 {
457 	struct nl_parsed_labels parsed_labels = { };
458 	int error;
459 
460 	/* Assumes target points to the beginning of the structure */
461 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &rule_labels_parser, npt, &parsed_labels);
462 	if (error != 0)
463 		return (error);
464 
465 	memcpy(target, parsed_labels.labels, sizeof(parsed_labels));
466 
467 	return (0);
468 }
469 
470 #define _OUT(_field)	offsetof(struct pf_kpool, _field)
471 static const struct nlattr_parser nla_p_pool[] = {
472 	{ .type = PF_PT_KEY, .off = _OUT(key), .arg = (void *)sizeof(struct pf_poolhashkey), .cb = nlattr_get_bytes },
473 	{ .type = PF_PT_COUNTER, .off = _OUT(counter), .cb = nlattr_get_in6_addr },
474 	{ .type = PF_PT_TBLIDX, .off = _OUT(tblidx), .cb = nlattr_get_uint32 },
475 	{ .type = PF_PT_PROXY_SRC_PORT, .off = _OUT(proxy_port[0]), .cb = nlattr_get_uint16 },
476 	{ .type = PF_PT_PROXY_DST_PORT, .off = _OUT(proxy_port[1]), .cb = nlattr_get_uint16 },
477 	{ .type = PF_PT_OPTS, .off = _OUT(opts), .cb = nlattr_get_uint8 },
478 	{ .type = PF_PT_MAPE, .off = _OUT(mape), .arg = &mape_portset_parser, .cb = nlattr_get_nested },
479 };
480 NL_DECLARE_ATTR_PARSER(pool_parser, nla_p_pool);
481 #undef _OUT
482 
483 #define _OUT(_field)	offsetof(struct pf_rule_uid, _field)
484 static const struct nlattr_parser nla_p_rule_uid[] = {
485 	{ .type = PF_RUT_UID_LOW, .off = _OUT(uid[0]), .cb = nlattr_get_uint32 },
486 	{ .type = PF_RUT_UID_HIGH, .off = _OUT(uid[1]), .cb = nlattr_get_uint32 },
487 	{ .type = PF_RUT_OP, .off = _OUT(op), .cb = nlattr_get_uint8 },
488 };
489 NL_DECLARE_ATTR_PARSER(rule_uid_parser, nla_p_rule_uid);
490 #undef _OUT
491 
492 struct nl_parsed_timeouts
493 {
494 	uint32_t	timeouts[PFTM_MAX];
495 	uint32_t	i;
496 };
497 
498 static int
499 nlattr_get_pf_timeout(struct nlattr *nla, struct nl_pstate *npt,
500     const void *arg, void *target)
501 {
502 	struct nl_parsed_timeouts *t = (struct nl_parsed_timeouts *)target;
503 	int ret;
504 
505 	if (t->i >= PFTM_MAX)
506 		return (E2BIG);
507 
508 	ret = nlattr_get_uint32(nla, npt, NULL, &t->timeouts[t->i]);
509 	if (ret == 0)
510 		t->i++;
511 
512 	return (ret);
513 }
514 
515 #define _OUT(_field)	offsetof(struct nl_parsed_timeout, _field)
516 static const struct nlattr_parser nla_p_timeouts[] = {
517 	{ .type = PF_TT_TIMEOUT, .off = 0, .cb = nlattr_get_pf_timeout },
518 };
519 NL_DECLARE_ATTR_PARSER(timeout_parser, nla_p_timeouts);
520 #undef _OUT
521 
522 static int
523 nlattr_get_nested_timeouts(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
524 {
525 	struct nl_parsed_timeouts parsed_timeouts = { };
526 	int error;
527 
528 	/* Assumes target points to the beginning of the structure */
529 	error = nl_parse_header(NLA_DATA(nla), NLA_DATA_LEN(nla), &timeout_parser, npt, &parsed_timeouts);
530 	if (error != 0)
531 		return (error);
532 
533 	memcpy(target, parsed_timeouts.timeouts, sizeof(parsed_timeouts.timeouts));
534 
535 	return (0);
536 }
537 
538 #define _OUT(_field)	offsetof(struct pf_krule, _field)
539 static const struct nlattr_parser nla_p_rule[] = {
540 	{ .type = PF_RT_SRC, .off = _OUT(src), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
541 	{ .type = PF_RT_DST, .off = _OUT(dst), .arg = &rule_addr_parser,.cb = nlattr_get_nested },
542 	{ .type = PF_RT_RIDENTIFIER, .off = _OUT(ridentifier), .cb = nlattr_get_uint32 },
543 	{ .type = PF_RT_LABELS, .off = _OUT(label), .arg = &rule_labels_parser,.cb = nlattr_get_nested_pf_rule_labels },
544 	{ .type = PF_RT_IFNAME, .off = _OUT(ifname), .arg = (void *)IFNAMSIZ, .cb = nlattr_get_chara },
545 	{ .type = PF_RT_QNAME, .off = _OUT(qname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
546 	{ .type = PF_RT_PQNAME, .off = _OUT(pqname), .arg = (void *)PF_QNAME_SIZE, .cb = nlattr_get_chara },
547 	{ .type = PF_RT_TAGNAME, .off = _OUT(tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
548 	{ .type = PF_RT_MATCH_TAGNAME, .off = _OUT(match_tagname), .arg = (void *)PF_TAG_NAME_SIZE, .cb = nlattr_get_chara },
549 	{ .type = PF_RT_OVERLOAD_TBLNAME, .off = _OUT(overload_tblname), .arg = (void *)PF_TABLE_NAME_SIZE, .cb = nlattr_get_chara },
550 	{ .type = PF_RT_RPOOL, .off = _OUT(rpool), .arg = &pool_parser, .cb = nlattr_get_nested },
551 	{ .type = PF_RT_OS_FINGERPRINT, .off = _OUT(os_fingerprint), .cb = nlattr_get_uint32 },
552 	{ .type = PF_RT_RTABLEID, .off = _OUT(rtableid), .cb = nlattr_get_uint32 },
553 	{ .type = PF_RT_TIMEOUT, .off = _OUT(timeout), .arg = &timeout_parser, .cb = nlattr_get_nested_timeouts },
554 	{ .type = PF_RT_MAX_STATES, .off = _OUT(max_states), .cb = nlattr_get_uint32 },
555 	{ .type = PF_RT_MAX_SRC_NODES, .off = _OUT(max_src_nodes), .cb = nlattr_get_uint32 },
556 	{ .type = PF_RT_MAX_SRC_STATES, .off = _OUT(max_src_states), .cb = nlattr_get_uint32 },
557 	{ .type = PF_RT_MAX_SRC_CONN_RATE_LIMIT, .off = _OUT(max_src_conn_rate.limit), .cb = nlattr_get_uint32 },
558 	{ .type = PF_RT_MAX_SRC_CONN_RATE_SECS, .off = _OUT(max_src_conn_rate.seconds), .cb = nlattr_get_uint32 },
559 	{ .type = PF_RT_DNPIPE, .off = _OUT(dnpipe), .cb = nlattr_get_uint16 },
560 	{ .type = PF_RT_DNRPIPE, .off = _OUT(dnrpipe), .cb = nlattr_get_uint16 },
561 	{ .type = PF_RT_DNFLAGS, .off = _OUT(free_flags), .cb = nlattr_get_uint32 },
562 	{ .type = PF_RT_NR, .off = _OUT(nr), .cb = nlattr_get_uint32 },
563 	{ .type = PF_RT_PROB, .off = _OUT(prob), .cb = nlattr_get_uint32 },
564 	{ .type = PF_RT_CUID, .off = _OUT(cuid), .cb = nlattr_get_uint32 },
565 	{. type = PF_RT_CPID, .off = _OUT(cpid), .cb = nlattr_get_uint32 },
566 	{ .type = PF_RT_RETURN_ICMP, .off = _OUT(return_icmp), .cb = nlattr_get_uint16 },
567 	{ .type = PF_RT_RETURN_ICMP6, .off = _OUT(return_icmp6), .cb = nlattr_get_uint16 },
568 	{ .type = PF_RT_MAX_MSS, .off = _OUT(max_mss), .cb = nlattr_get_uint16 },
569 	{ .type = PF_RT_SCRUB_FLAGS, .off = _OUT(scrub_flags), .cb = nlattr_get_uint16 },
570 	{ .type = PF_RT_UID, .off = _OUT(uid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
571 	{ .type = PF_RT_GID, .off = _OUT(gid), .arg = &rule_uid_parser, .cb = nlattr_get_nested },
572 	{ .type = PF_RT_RULE_FLAG, .off = _OUT(rule_flag), .cb = nlattr_get_uint32 },
573 	{ .type = PF_RT_ACTION, .off = _OUT(action), .cb = nlattr_get_uint8 },
574 	{ .type = PF_RT_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 },
575 	{ .type = PF_RT_LOG, .off = _OUT(log), .cb = nlattr_get_uint8 },
576 	{ .type = PF_RT_LOGIF, .off = _OUT(logif), .cb = nlattr_get_uint8 },
577 	{ .type = PF_RT_QUICK, .off = _OUT(quick), .cb = nlattr_get_uint8 },
578 	{ .type = PF_RT_IF_NOT, .off = _OUT(ifnot), .cb = nlattr_get_uint8 },
579 	{ .type = PF_RT_MATCH_TAG_NOT, .off = _OUT(match_tag_not), .cb = nlattr_get_uint8 },
580 	{ .type = PF_RT_NATPASS, .off = _OUT(natpass), .cb = nlattr_get_uint8 },
581 	{ .type = PF_RT_KEEP_STATE, .off = _OUT(keep_state), .cb = nlattr_get_uint8 },
582 	{ .type = PF_RT_AF, .off = _OUT(af), .cb = nlattr_get_uint8 },
583 	{ .type = PF_RT_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 },
584 	{ .type = PF_RT_TYPE, .off = _OUT(type), .cb = nlattr_get_uint8 },
585 	{ .type = PF_RT_CODE, .off = _OUT(code), .cb = nlattr_get_uint8 },
586 	{ .type = PF_RT_FLAGS, .off = _OUT(flags), .cb = nlattr_get_uint8 },
587 	{ .type = PF_RT_FLAGSET, .off = _OUT(flagset), .cb = nlattr_get_uint8 },
588 	{ .type = PF_RT_MIN_TTL, .off = _OUT(min_ttl), .cb = nlattr_get_uint8 },
589 	{ .type = PF_RT_ALLOW_OPTS, .off = _OUT(allow_opts), .cb = nlattr_get_uint8 },
590 	{ .type = PF_RT_RT, .off = _OUT(rt), .cb = nlattr_get_uint8 },
591 	{ .type = PF_RT_RETURN_TTL, .off = _OUT(return_ttl), .cb = nlattr_get_uint8 },
592 	{ .type = PF_RT_TOS, .off = _OUT(tos), .cb = nlattr_get_uint8 },
593 	{ .type = PF_RT_SET_TOS, .off = _OUT(set_tos), .cb = nlattr_get_uint8 },
594 	{ .type = PF_RT_ANCHOR_RELATIVE, .off = _OUT(anchor_relative), .cb = nlattr_get_uint8 },
595 	{ .type = PF_RT_ANCHOR_WILDCARD, .off = _OUT(anchor_wildcard), .cb = nlattr_get_uint8 },
596 	{ .type = PF_RT_FLUSH, .off = _OUT(flush), .cb = nlattr_get_uint8 },
597 	{ .type = PF_RT_PRIO, .off = _OUT(prio), .cb = nlattr_get_uint8 },
598 	{ .type = PF_RT_SET_PRIO, .off = _OUT(set_prio[0]), .cb = nlattr_get_uint8 },
599 	{ .type = PF_RT_SET_PRIO_REPLY, .off = _OUT(set_prio[1]), .cb = nlattr_get_uint8 },
600 	{ .type = PF_RT_DIVERT_ADDRESS, .off = _OUT(divert.addr), .cb = nlattr_get_in6_addr },
601 	{ .type = PF_RT_DIVERT_PORT, .off = _OUT(divert.port), .cb = nlattr_get_uint16 },
602 };
603 NL_DECLARE_ATTR_PARSER(rule_parser, nla_p_rule);
604 #undef _OUT
605 struct nl_parsed_addrule {
606 	struct pf_krule	*rule;
607 	uint32_t	 ticket;
608 	uint32_t	 pool_ticket;
609 	char		*anchor;
610 	char		*anchor_call;
611 };
612 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
613 #define	_OUT(_field)	offsetof(struct nl_parsed_addrule, _field)
614 static const struct nlattr_parser nla_p_addrule[] = {
615 	{ .type = PF_ART_TICKET, .off = _OUT(ticket), .cb = nlattr_get_uint32 },
616 	{ .type = PF_ART_POOL_TICKET, .off = _OUT(pool_ticket), .cb = nlattr_get_uint32 },
617 	{ .type = PF_ART_ANCHOR, .off = _OUT(anchor), .cb = nlattr_get_string },
618 	{ .type = PF_ART_ANCHOR_CALL, .off = _OUT(anchor_call), .cb = nlattr_get_string },
619 	{ .type = PF_ART_RULE, .off = _OUT(rule), .arg = &rule_parser, .cb = nlattr_get_nested_ptr }
620 };
621 static const struct nlfield_parser nlf_p_addrule[] = {
622 };
623 #undef _IN
624 #undef _OUT
625 NL_DECLARE_PARSER(addrule_parser, struct genlmsghdr, nlf_p_addrule, nla_p_addrule);
626 
627 static int
628 pf_handle_addrule(struct nlmsghdr *hdr, struct nl_pstate *npt)
629 {
630 	int error;
631 	struct nl_parsed_addrule attrs = {};
632 
633 	attrs.rule = pf_krule_alloc();
634 
635 	error = nl_parse_nlmsg(hdr, &addrule_parser, npt, &attrs);
636 	if (error != 0) {
637 		pf_free_rule(attrs.rule);
638 		return (error);
639 	}
640 
641 	error = pf_ioctl_addrule(attrs.rule, attrs.ticket, attrs.pool_ticket,
642 	    attrs.anchor, attrs.anchor_call, nlp_get_cred(npt->nlp)->cr_uid,
643 	    hdr->nlmsg_pid);
644 
645 	return (error);
646 }
647 
648 struct nl_parsed_getrules {
649 	char		*anchor;
650 	uint8_t		 action;
651 };
652 #define	_IN(_field)	offsetof(struct genlmsghdr, _field)
653 #define	_OUT(_field)	offsetof(struct pfioc_rule, _field)
654 static const struct nlattr_parser nla_p_getrules[] = {
655 	{ .type = PF_GR_ANCHOR, .off = _OUT(anchor), .arg = (void *)MAXPATHLEN, .cb = nlattr_get_chara },
656 	{ .type = PF_GR_ACTION, .off = _OUT(rule.action), .cb = nlattr_get_uint8 },
657 };
658 static const struct nlfield_parser nlf_p_getrules[] = {
659 };
660 NL_DECLARE_PARSER(getrules_parser, struct genlmsghdr, nlf_p_getrules, nla_p_getrules);
661 
662 static int
663 pf_handle_getrules(struct nlmsghdr *hdr, struct nl_pstate *npt)
664 {
665 	struct pfioc_rule attrs = {};
666 	int error;
667 	struct nl_writer *nw = npt->nw;
668 	struct genlmsghdr *ghdr_new;
669 
670 	error = nl_parse_nlmsg(hdr, &getrules_parser, npt, &attrs);
671 	if (error != 0)
672 		return (error);
673 
674 	if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr)))
675 		return (ENOMEM);
676 
677 	ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr);
678 	ghdr_new->cmd = PFNL_CMD_GETRULES;
679 	ghdr_new->version = 0;
680 	ghdr_new->reserved = 0;
681 
682 	error = pf_ioctl_getrules(&attrs);
683 	if (error != 0)
684 		goto out;
685 
686 	nlattr_add_u32(nw, PF_GR_NR, attrs.nr);
687 	nlattr_add_u32(nw, PF_GR_TICKET, attrs.ticket);
688 
689 	if (!nlmsg_end(nw)) {
690 		error = ENOMEM;
691 		goto out;
692 	}
693 
694 	return (0);
695 
696 out:
697 	nlmsg_abort(nw);
698 	return (error);
699 }
700 
701 static const struct nlhdr_parser *all_parsers[] = {
702 	&state_parser,
703 	&addrule_parser,
704 	&getrules_parser
705 };
706 
707 static int family_id;
708 
709 static const struct genl_cmd pf_cmds[] = {
710 	{
711 		.cmd_num = PFNL_CMD_GETSTATES,
712 		.cmd_name = "GETSTATES",
713 		.cmd_cb = pf_handle_getstates,
714 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
715 	},
716 	{
717 		.cmd_num = PFNL_CMD_GETCREATORS,
718 		.cmd_name = "GETCREATORS",
719 		.cmd_cb = pf_handle_getcreators,
720 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
721 	},
722 	{
723 		.cmd_num = PFNL_CMD_START,
724 		.cmd_name = "START",
725 		.cmd_cb = pf_handle_start,
726 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
727 	},
728 	{
729 		.cmd_num = PFNL_CMD_STOP,
730 		.cmd_name = "STOP",
731 		.cmd_cb = pf_handle_stop,
732 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL,
733 	},
734 	{
735 		.cmd_num = PFNL_CMD_ADDRULE,
736 		.cmd_name = "ADDRULE",
737 		.cmd_cb = pf_handle_addrule,
738 		.cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
739 	},
740 	{
741 		.cmd_num = PFNL_CMD_GETRULES,
742 		.cmd_name = "GETRULES",
743 		.cmd_cb = pf_handle_getrules,
744 		.cmd_flags = GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL,
745 	},
746 };
747 
748 void
749 pf_nl_register(void)
750 {
751 	NL_VERIFY_PARSERS(all_parsers);
752 
753 	family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX);
754 	genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds));
755 }
756 
757 void
758 pf_nl_unregister(void)
759 {
760 	genl_unregister_family(PFNL_FAMILY_NAME);
761 }
762