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