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/cdefs.h> 31 32 #include <sys/param.h> 33 #include <sys/malloc.h> 34 #include <sys/mbuf.h> 35 #include <sys/socket.h> 36 37 #include <net/pfvar.h> 38 39 #include <netlink/netlink.h> 40 #include <netlink/netlink_ctl.h> 41 #include <netlink/netlink_generic.h> 42 #include <netlink/netlink_message_writer.h> 43 44 #include <netpfil/pf/pf_nl.h> 45 46 #define DEBUG_MOD_NAME nl_pf 47 #define DEBUG_MAX_LEVEL LOG_DEBUG3 48 #include <netlink/netlink_debug.h> 49 _DECLARE_DEBUG(LOG_DEBUG); 50 51 struct nl_parsed_state { 52 uint8_t version; 53 uint32_t id; 54 uint32_t creatorid; 55 }; 56 57 #define _IN(_field) offsetof(struct genlmsghdr, _field) 58 #define _OUT(_field) offsetof(struct nl_parsed_state, _field) 59 static const struct nlattr_parser nla_p_state[] = { 60 { .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 }, 61 { .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 }, 62 }; 63 static const struct nlfield_parser nlf_p_generic[] = { 64 { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, 65 }; 66 #undef _IN 67 #undef _OUT 68 NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state); 69 70 static void 71 dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af) 72 { 73 switch (af) { 74 case AF_INET: 75 nlattr_add(nw, attr, 4, &addr->v4); 76 break; 77 case AF_INET6: 78 nlattr_add(nw, attr, 16, &addr->v6); 79 break; 80 }; 81 } 82 83 static bool 84 dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer) 85 { 86 int off = nlattr_add_nested(nw, attr); 87 if (off == 0) 88 return (false); 89 90 nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo); 91 nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi); 92 nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff); 93 nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win); 94 nlattr_add_u16(nw, PF_STP_MSS, peer->mss); 95 nlattr_add_u8(nw, PF_STP_STATE, peer->state); 96 nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale); 97 98 if (peer->scrub != NULL) { 99 struct pf_state_scrub *sc = peer->scrub; 100 uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP; 101 102 nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags); 103 nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod); 104 nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl); 105 nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID); 106 } 107 nlattr_set_len(nw, off); 108 109 return (true); 110 } 111 112 static bool 113 dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key) 114 { 115 int off = nlattr_add_nested(nw, attr); 116 if (off == 0) 117 return (false); 118 119 dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af); 120 dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af); 121 nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]); 122 nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]); 123 124 nlattr_set_len(nw, off); 125 126 return (true); 127 } 128 129 static int 130 dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s, 131 struct nl_pstate *npt) 132 { 133 struct nl_writer *nw = npt->nw; 134 int error = 0; 135 int af; 136 struct pf_state_key *key; 137 138 if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 139 goto enomem; 140 141 struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 142 ghdr_new->cmd = PFNL_CMD_GETSTATES; 143 ghdr_new->version = 0; 144 ghdr_new->reserved = 0; 145 146 nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION); 147 148 key = s->key[PF_SK_WIRE]; 149 if (!dump_state_key(nw, PF_ST_KEY_WIRE, key)) 150 goto enomem; 151 key = s->key[PF_SK_STACK]; 152 if (!dump_state_key(nw, PF_ST_KEY_STACK, key)) 153 goto enomem; 154 155 af = s->key[PF_SK_WIRE]->af; 156 nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto); 157 nlattr_add_u8(nw, PF_ST_AF, af); 158 159 nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name); 160 nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name); 161 dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af); 162 nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - s->creation); 163 uint32_t expire = pf_state_expires(s); 164 if (expire > time_uptime) 165 expire = expire - time_uptime; 166 nlattr_add_u32(nw, PF_ST_EXPIRE, expire); 167 nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction); 168 nlattr_add_u8(nw, PF_ST_LOG, s->act.log); 169 nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout); 170 nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags); 171 uint8_t sync_flags = 0; 172 if (s->src_node) 173 sync_flags |= PFSYNC_FLAG_SRCNODE; 174 if (s->nat_src_node) 175 sync_flags |= PFSYNC_FLAG_NATSRCNODE; 176 nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags); 177 nlattr_add_u64(nw, PF_ST_ID, s->id); 178 nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid)); 179 180 nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1); 181 nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1); 182 nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1); 183 184 nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]); 185 nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]); 186 nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]); 187 nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]); 188 189 if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src)) 190 goto enomem; 191 if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst)) 192 goto enomem; 193 194 if (nlmsg_end(nw)) 195 return (0); 196 197 enomem: 198 error = ENOMEM; 199 nlmsg_abort(nw); 200 return (error); 201 } 202 203 static int 204 handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs, 205 struct nlmsghdr *hdr, struct nl_pstate *npt) 206 { 207 int error = 0; 208 209 hdr->nlmsg_flags |= NLM_F_MULTI; 210 211 for (int i = 0; i <= pf_hashmask; i++) { 212 struct pf_idhash *ih = &V_pf_idhash[i]; 213 struct pf_kstate *s; 214 215 if (LIST_EMPTY(&ih->states)) 216 continue; 217 218 PF_HASHROW_LOCK(ih); 219 LIST_FOREACH(s, &ih->states, entry) { 220 if (s->timeout != PFTM_UNLINKED) { 221 error = dump_state(nlp, hdr, s, npt); 222 if (error != 0) 223 break; 224 } 225 } 226 PF_HASHROW_UNLOCK(ih); 227 } 228 229 if (!nlmsg_end_dump(npt->nw, error, hdr)) { 230 NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 231 return (ENOMEM); 232 } 233 234 return (error); 235 } 236 237 static int 238 handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs, 239 struct nlmsghdr *hdr, struct nl_pstate *npt) 240 { 241 struct pf_kstate *s = pf_find_state_byid(attrs->id, attrs->creatorid); 242 if (s == NULL) 243 return (ENOENT); 244 return (dump_state(nlp, hdr, s, npt)); 245 } 246 247 static int 248 dump_creatorid(struct nlpcb *nlp, const struct nlmsghdr *hdr, uint32_t creator, 249 struct nl_pstate *npt) 250 { 251 struct nl_writer *nw = npt->nw; 252 253 if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 254 goto enomem; 255 256 struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 257 ghdr_new->cmd = PFNL_CMD_GETCREATORS; 258 ghdr_new->version = 0; 259 ghdr_new->reserved = 0; 260 261 nlattr_add_u32(nw, PF_ST_CREATORID, htonl(creator)); 262 263 if (nlmsg_end(nw)) 264 return (0); 265 266 enomem: 267 nlmsg_abort(nw); 268 return (ENOMEM); 269 } 270 271 static int 272 pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt) 273 { 274 int error; 275 276 struct nl_parsed_state attrs = {}; 277 error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs); 278 if (error != 0) 279 return (error); 280 281 if (attrs.id != 0) 282 error = handle_getstate(npt->nlp, &attrs, hdr, npt); 283 else 284 error = handle_dumpstates(npt->nlp, &attrs, hdr, npt); 285 286 return (error); 287 } 288 289 static int 290 pf_handle_getcreators(struct nlmsghdr *hdr, struct nl_pstate *npt) 291 { 292 uint32_t creators[16]; 293 int error = 0; 294 295 bzero(creators, sizeof(creators)); 296 297 for (int i = 0; i < pf_hashmask; i++) { 298 struct pf_idhash *ih = &V_pf_idhash[i]; 299 struct pf_kstate *s; 300 301 if (LIST_EMPTY(&ih->states)) 302 continue; 303 304 PF_HASHROW_LOCK(ih); 305 LIST_FOREACH(s, &ih->states, entry) { 306 int j; 307 if (s->timeout == PFTM_UNLINKED) 308 continue; 309 310 for (j = 0; j < nitems(creators); j++) { 311 if (creators[j] == s->creatorid) 312 break; 313 if (creators[j] == 0) { 314 creators[j] = s->creatorid; 315 break; 316 } 317 } 318 if (j == nitems(creators)) 319 printf("Warning: too many creators!\n"); 320 } 321 PF_HASHROW_UNLOCK(ih); 322 } 323 324 hdr->nlmsg_flags |= NLM_F_MULTI; 325 for (int i = 0; i < nitems(creators); i++) { 326 if (creators[i] == 0) 327 break; 328 error = dump_creatorid(npt->nlp, hdr, creators[i], npt); 329 } 330 331 if (!nlmsg_end_dump(npt->nw, error, hdr)) { 332 NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 333 return (ENOMEM); 334 } 335 336 return (error); 337 } 338 339 static int 340 pf_handle_start(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused) 341 { 342 return (pf_start()); 343 } 344 345 static int 346 pf_handle_stop(struct nlmsghdr *hdr __unused, struct nl_pstate *npt __unused) 347 { 348 return (pf_stop()); 349 } 350 351 static const struct nlhdr_parser *all_parsers[] = { &state_parser }; 352 353 static int family_id; 354 355 static const struct genl_cmd pf_cmds[] = { 356 { 357 .cmd_num = PFNL_CMD_GETSTATES, 358 .cmd_name = "GETSTATES", 359 .cmd_cb = pf_handle_getstates, 360 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 361 }, 362 { 363 .cmd_num = PFNL_CMD_GETCREATORS, 364 .cmd_name = "GETCREATORS", 365 .cmd_cb = pf_handle_getcreators, 366 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 367 }, 368 { 369 .cmd_num = PFNL_CMD_START, 370 .cmd_name = "START", 371 .cmd_cb = pf_handle_start, 372 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, 373 }, 374 { 375 .cmd_num = PFNL_CMD_STOP, 376 .cmd_name = "STOP", 377 .cmd_cb = pf_handle_stop, 378 .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_HASPOL, 379 }, 380 }; 381 382 void 383 pf_nl_register(void) 384 { 385 NL_VERIFY_PARSERS(all_parsers); 386 family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX); 387 genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds)); 388 } 389 390 void 391 pf_nl_unregister(void) 392 { 393 genl_unregister_family(PFNL_FAMILY_NAME); 394 } 395