1*2cef6288SAlexander V. Chernikov /*- 2*2cef6288SAlexander V. Chernikov * SPDX-License-Identifier: BSD-2-Clause 3*2cef6288SAlexander V. Chernikov * 4*2cef6288SAlexander V. Chernikov * Copyright (c) 2023 Alexander V. Chernikov <melifaro@FreeBSD.org> 5*2cef6288SAlexander V. Chernikov * Copyright (c) 2023 Rubicon Communications, LLC (Netgate) 6*2cef6288SAlexander V. Chernikov * 7*2cef6288SAlexander V. Chernikov * Redistribution and use in source and binary forms, with or without 8*2cef6288SAlexander V. Chernikov * modification, are permitted provided that the following conditions 9*2cef6288SAlexander V. Chernikov * are met: 10*2cef6288SAlexander V. Chernikov * 1. Redistributions of source code must retain the above copyright 11*2cef6288SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer. 12*2cef6288SAlexander V. Chernikov * 2. Redistributions in binary form must reproduce the above copyright 13*2cef6288SAlexander V. Chernikov * notice, this list of conditions and the following disclaimer in the 14*2cef6288SAlexander V. Chernikov * documentation and/or other materials provided with the distribution. 15*2cef6288SAlexander V. Chernikov * 16*2cef6288SAlexander V. Chernikov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17*2cef6288SAlexander V. Chernikov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*2cef6288SAlexander V. Chernikov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*2cef6288SAlexander V. Chernikov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20*2cef6288SAlexander V. Chernikov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*2cef6288SAlexander V. Chernikov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22*2cef6288SAlexander V. Chernikov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23*2cef6288SAlexander V. Chernikov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24*2cef6288SAlexander V. Chernikov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25*2cef6288SAlexander V. Chernikov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26*2cef6288SAlexander V. Chernikov * SUCH DAMAGE. 27*2cef6288SAlexander V. Chernikov * 28*2cef6288SAlexander V. Chernikov */ 29*2cef6288SAlexander V. Chernikov 30*2cef6288SAlexander V. Chernikov #include "opt_netlink.h" 31*2cef6288SAlexander V. Chernikov 32*2cef6288SAlexander V. Chernikov #include <sys/cdefs.h> 33*2cef6288SAlexander V. Chernikov 34*2cef6288SAlexander V. Chernikov #include <sys/param.h> 35*2cef6288SAlexander V. Chernikov #include <sys/malloc.h> 36*2cef6288SAlexander V. Chernikov #include <sys/mbuf.h> 37*2cef6288SAlexander V. Chernikov #include <sys/socket.h> 38*2cef6288SAlexander V. Chernikov 39*2cef6288SAlexander V. Chernikov #include <net/pfvar.h> 40*2cef6288SAlexander V. Chernikov 41*2cef6288SAlexander V. Chernikov #include <netlink/netlink.h> 42*2cef6288SAlexander V. Chernikov #include <netlink/netlink_ctl.h> 43*2cef6288SAlexander V. Chernikov #include <netlink/netlink_generic.h> 44*2cef6288SAlexander V. Chernikov #include <netlink/netlink_message_writer.h> 45*2cef6288SAlexander V. Chernikov 46*2cef6288SAlexander V. Chernikov #include <netpfil/pf/pf_nl.h> 47*2cef6288SAlexander V. Chernikov 48*2cef6288SAlexander V. Chernikov #define DEBUG_MOD_NAME nl_pf 49*2cef6288SAlexander V. Chernikov #define DEBUG_MAX_LEVEL LOG_DEBUG3 50*2cef6288SAlexander V. Chernikov #include <netlink/netlink_debug.h> 51*2cef6288SAlexander V. Chernikov _DECLARE_DEBUG(LOG_DEBUG); 52*2cef6288SAlexander V. Chernikov 53*2cef6288SAlexander V. Chernikov struct nl_parsed_state { 54*2cef6288SAlexander V. Chernikov uint8_t version; 55*2cef6288SAlexander V. Chernikov uint32_t id; 56*2cef6288SAlexander V. Chernikov uint32_t creatorid; 57*2cef6288SAlexander V. Chernikov }; 58*2cef6288SAlexander V. Chernikov 59*2cef6288SAlexander V. Chernikov #define _IN(_field) offsetof(struct genlmsghdr, _field) 60*2cef6288SAlexander V. Chernikov #define _OUT(_field) offsetof(struct nl_parsed_state, _field) 61*2cef6288SAlexander V. Chernikov static const struct nlattr_parser nla_p_state[] = { 62*2cef6288SAlexander V. Chernikov { .type = PF_ST_ID, .off = _OUT(id), .cb = nlattr_get_uint32 }, 63*2cef6288SAlexander V. Chernikov { .type = PF_ST_CREATORID, .off = _OUT(creatorid), .cb = nlattr_get_uint32 }, 64*2cef6288SAlexander V. Chernikov }; 65*2cef6288SAlexander V. Chernikov static const struct nlfield_parser nlf_p_generic[] = { 66*2cef6288SAlexander V. Chernikov { .off_in = _IN(version), .off_out = _OUT(version), .cb = nlf_get_u8 }, 67*2cef6288SAlexander V. Chernikov }; 68*2cef6288SAlexander V. Chernikov #undef _IN 69*2cef6288SAlexander V. Chernikov #undef _OUT 70*2cef6288SAlexander V. Chernikov NL_DECLARE_PARSER(state_parser, struct genlmsghdr, nlf_p_generic, nla_p_state); 71*2cef6288SAlexander V. Chernikov 72*2cef6288SAlexander V. Chernikov static void 73*2cef6288SAlexander V. Chernikov dump_addr(struct nl_writer *nw, int attr, const struct pf_addr *addr, int af) 74*2cef6288SAlexander V. Chernikov { 75*2cef6288SAlexander V. Chernikov switch (af) { 76*2cef6288SAlexander V. Chernikov case AF_INET: 77*2cef6288SAlexander V. Chernikov nlattr_add(nw, attr, 4, &addr->v4); 78*2cef6288SAlexander V. Chernikov break; 79*2cef6288SAlexander V. Chernikov case AF_INET6: 80*2cef6288SAlexander V. Chernikov nlattr_add(nw, attr, 16, &addr->v6); 81*2cef6288SAlexander V. Chernikov break; 82*2cef6288SAlexander V. Chernikov }; 83*2cef6288SAlexander V. Chernikov } 84*2cef6288SAlexander V. Chernikov 85*2cef6288SAlexander V. Chernikov static bool 86*2cef6288SAlexander V. Chernikov dump_state_peer(struct nl_writer *nw, int attr, const struct pf_state_peer *peer) 87*2cef6288SAlexander V. Chernikov { 88*2cef6288SAlexander V. Chernikov int off = nlattr_add_nested(nw, attr); 89*2cef6288SAlexander V. Chernikov if (off == 0) 90*2cef6288SAlexander V. Chernikov return (false); 91*2cef6288SAlexander V. Chernikov 92*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_STP_SEQLO, peer->seqlo); 93*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_STP_SEQHI, peer->seqhi); 94*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_STP_SEQDIFF, peer->seqdiff); 95*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_STP_MAX_WIN, peer->max_win); 96*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_STP_MSS, peer->mss); 97*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_STP_STATE, peer->state); 98*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_STP_WSCALE, peer->wscale); 99*2cef6288SAlexander V. Chernikov 100*2cef6288SAlexander V. Chernikov if (peer->scrub != NULL) { 101*2cef6288SAlexander V. Chernikov struct pf_state_scrub *sc = peer->scrub; 102*2cef6288SAlexander V. Chernikov uint16_t pfss_flags = sc->pfss_flags & PFSS_TIMESTAMP; 103*2cef6288SAlexander V. Chernikov 104*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_STP_PFSS_FLAGS, pfss_flags); 105*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_STP_PFSS_TS_MOD, sc->pfss_ts_mod); 106*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_STP_PFSS_TTL, sc->pfss_ttl); 107*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_STP_SCRUB_FLAG, PFSYNC_SCRUB_FLAG_VALID); 108*2cef6288SAlexander V. Chernikov } 109*2cef6288SAlexander V. Chernikov nlattr_set_len(nw, off); 110*2cef6288SAlexander V. Chernikov 111*2cef6288SAlexander V. Chernikov return (true); 112*2cef6288SAlexander V. Chernikov } 113*2cef6288SAlexander V. Chernikov 114*2cef6288SAlexander V. Chernikov static bool 115*2cef6288SAlexander V. Chernikov dump_state_key(struct nl_writer *nw, int attr, const struct pf_state_key *key) 116*2cef6288SAlexander V. Chernikov { 117*2cef6288SAlexander V. Chernikov int off = nlattr_add_nested(nw, attr); 118*2cef6288SAlexander V. Chernikov if (off == 0) 119*2cef6288SAlexander V. Chernikov return (false); 120*2cef6288SAlexander V. Chernikov 121*2cef6288SAlexander V. Chernikov dump_addr(nw, PF_STK_ADDR0, &key->addr[0], key->af); 122*2cef6288SAlexander V. Chernikov dump_addr(nw, PF_STK_ADDR1, &key->addr[1], key->af); 123*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_STK_PORT0, key->port[0]); 124*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_STK_PORT1, key->port[1]); 125*2cef6288SAlexander V. Chernikov 126*2cef6288SAlexander V. Chernikov nlattr_set_len(nw, off); 127*2cef6288SAlexander V. Chernikov 128*2cef6288SAlexander V. Chernikov return (true); 129*2cef6288SAlexander V. Chernikov } 130*2cef6288SAlexander V. Chernikov 131*2cef6288SAlexander V. Chernikov static int 132*2cef6288SAlexander V. Chernikov dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s, 133*2cef6288SAlexander V. Chernikov struct nl_pstate *npt) 134*2cef6288SAlexander V. Chernikov { 135*2cef6288SAlexander V. Chernikov struct nl_writer *nw = npt->nw; 136*2cef6288SAlexander V. Chernikov int error = 0; 137*2cef6288SAlexander V. Chernikov int af; 138*2cef6288SAlexander V. Chernikov struct pf_state_key *key; 139*2cef6288SAlexander V. Chernikov 140*2cef6288SAlexander V. Chernikov if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) 141*2cef6288SAlexander V. Chernikov goto enomem; 142*2cef6288SAlexander V. Chernikov 143*2cef6288SAlexander V. Chernikov struct genlmsghdr *ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); 144*2cef6288SAlexander V. Chernikov ghdr_new->cmd = PFNL_CMD_GETSTATES; 145*2cef6288SAlexander V. Chernikov ghdr_new->version = 0; 146*2cef6288SAlexander V. Chernikov ghdr_new->reserved = 0; 147*2cef6288SAlexander V. Chernikov 148*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_VERSION, PF_STATE_VERSION); 149*2cef6288SAlexander V. Chernikov 150*2cef6288SAlexander V. Chernikov key = s->key[PF_SK_WIRE]; 151*2cef6288SAlexander V. Chernikov if (!dump_state_key(nw, PF_ST_KEY_WIRE, key)) 152*2cef6288SAlexander V. Chernikov goto enomem; 153*2cef6288SAlexander V. Chernikov key = s->key[PF_SK_STACK]; 154*2cef6288SAlexander V. Chernikov if (!dump_state_key(nw, PF_ST_KEY_STACK, key)) 155*2cef6288SAlexander V. Chernikov goto enomem; 156*2cef6288SAlexander V. Chernikov 157*2cef6288SAlexander V. Chernikov af = s->key[PF_SK_WIRE]->af; 158*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_PROTO, s->key[PF_SK_WIRE]->proto); 159*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_AF, af); 160*2cef6288SAlexander V. Chernikov 161*2cef6288SAlexander V. Chernikov nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name); 162*2cef6288SAlexander V. Chernikov nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name); 163*2cef6288SAlexander V. Chernikov dump_addr(nw, PF_ST_RT_ADDR, &s->rt_addr, af); 164*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - s->creation); 165*2cef6288SAlexander V. Chernikov uint32_t expire = pf_state_expires(s); 166*2cef6288SAlexander V. Chernikov if (expire > time_uptime) 167*2cef6288SAlexander V. Chernikov expire = expire - time_uptime; 168*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_EXPIRE, expire); 169*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_DIRECTION, s->direction); 170*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_LOG, s->act.log); 171*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout); 172*2cef6288SAlexander V. Chernikov nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags); 173*2cef6288SAlexander V. Chernikov uint8_t sync_flags = 0; 174*2cef6288SAlexander V. Chernikov if (s->src_node) 175*2cef6288SAlexander V. Chernikov sync_flags |= PFSYNC_FLAG_SRCNODE; 176*2cef6288SAlexander V. Chernikov if (s->nat_src_node) 177*2cef6288SAlexander V. Chernikov sync_flags |= PFSYNC_FLAG_NATSRCNODE; 178*2cef6288SAlexander V. Chernikov nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags); 179*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_ID, s->id); 180*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid)); 181*2cef6288SAlexander V. Chernikov 182*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_RULE, s->rule.ptr ? s->rule.ptr->nr : -1); 183*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_ANCHOR, s->anchor.ptr ? s->anchor.ptr->nr : -1); 184*2cef6288SAlexander V. Chernikov nlattr_add_u32(nw, PF_ST_NAT_RULE, s->nat_rule.ptr ? s->nat_rule.ptr->nr : -1); 185*2cef6288SAlexander V. Chernikov 186*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_PACKETS0, s->packets[0]); 187*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_PACKETS1, s->packets[1]); 188*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_BYTES0, s->bytes[0]); 189*2cef6288SAlexander V. Chernikov nlattr_add_u64(nw, PF_ST_BYTES1, s->bytes[1]); 190*2cef6288SAlexander V. Chernikov 191*2cef6288SAlexander V. Chernikov if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src)) 192*2cef6288SAlexander V. Chernikov goto enomem; 193*2cef6288SAlexander V. Chernikov if (!dump_state_peer(nw, PF_ST_PEER_DST, &s->dst)) 194*2cef6288SAlexander V. Chernikov goto enomem; 195*2cef6288SAlexander V. Chernikov 196*2cef6288SAlexander V. Chernikov if (nlmsg_end(nw)) 197*2cef6288SAlexander V. Chernikov return (0); 198*2cef6288SAlexander V. Chernikov 199*2cef6288SAlexander V. Chernikov enomem: 200*2cef6288SAlexander V. Chernikov error = ENOMEM; 201*2cef6288SAlexander V. Chernikov nlmsg_abort(nw); 202*2cef6288SAlexander V. Chernikov return (error); 203*2cef6288SAlexander V. Chernikov } 204*2cef6288SAlexander V. Chernikov 205*2cef6288SAlexander V. Chernikov static int 206*2cef6288SAlexander V. Chernikov handle_dumpstates(struct nlpcb *nlp, struct nl_parsed_state *attrs, 207*2cef6288SAlexander V. Chernikov struct nlmsghdr *hdr, struct nl_pstate *npt) 208*2cef6288SAlexander V. Chernikov { 209*2cef6288SAlexander V. Chernikov int error = 0; 210*2cef6288SAlexander V. Chernikov 211*2cef6288SAlexander V. Chernikov hdr->nlmsg_flags |= NLM_F_MULTI; 212*2cef6288SAlexander V. Chernikov 213*2cef6288SAlexander V. Chernikov for (int i = 0; i <= pf_hashmask; i++) { 214*2cef6288SAlexander V. Chernikov struct pf_idhash *ih = &V_pf_idhash[i]; 215*2cef6288SAlexander V. Chernikov struct pf_kstate *s; 216*2cef6288SAlexander V. Chernikov 217*2cef6288SAlexander V. Chernikov if (LIST_EMPTY(&ih->states)) 218*2cef6288SAlexander V. Chernikov continue; 219*2cef6288SAlexander V. Chernikov 220*2cef6288SAlexander V. Chernikov PF_HASHROW_LOCK(ih); 221*2cef6288SAlexander V. Chernikov LIST_FOREACH(s, &ih->states, entry) { 222*2cef6288SAlexander V. Chernikov if (s->timeout != PFTM_UNLINKED) { 223*2cef6288SAlexander V. Chernikov error = dump_state(nlp, hdr, s, npt); 224*2cef6288SAlexander V. Chernikov if (error != 0) 225*2cef6288SAlexander V. Chernikov break; 226*2cef6288SAlexander V. Chernikov } 227*2cef6288SAlexander V. Chernikov } 228*2cef6288SAlexander V. Chernikov PF_HASHROW_UNLOCK(ih); 229*2cef6288SAlexander V. Chernikov } 230*2cef6288SAlexander V. Chernikov 231*2cef6288SAlexander V. Chernikov if (!nlmsg_end_dump(npt->nw, error, hdr)) { 232*2cef6288SAlexander V. Chernikov NL_LOG(LOG_DEBUG, "Unable to finalize the dump"); 233*2cef6288SAlexander V. Chernikov return (ENOMEM); 234*2cef6288SAlexander V. Chernikov } 235*2cef6288SAlexander V. Chernikov 236*2cef6288SAlexander V. Chernikov return (error); 237*2cef6288SAlexander V. Chernikov } 238*2cef6288SAlexander V. Chernikov 239*2cef6288SAlexander V. Chernikov static int 240*2cef6288SAlexander V. Chernikov handle_getstate(struct nlpcb *nlp, struct nl_parsed_state *attrs, 241*2cef6288SAlexander V. Chernikov struct nlmsghdr *hdr, struct nl_pstate *npt) 242*2cef6288SAlexander V. Chernikov { 243*2cef6288SAlexander V. Chernikov struct pf_kstate *s = pf_find_state_byid(attrs->id, attrs->creatorid); 244*2cef6288SAlexander V. Chernikov if (s == NULL) 245*2cef6288SAlexander V. Chernikov return (ENOENT); 246*2cef6288SAlexander V. Chernikov return (dump_state(nlp, hdr, s, npt)); 247*2cef6288SAlexander V. Chernikov } 248*2cef6288SAlexander V. Chernikov 249*2cef6288SAlexander V. Chernikov static int 250*2cef6288SAlexander V. Chernikov pf_handle_getstates(struct nlmsghdr *hdr, struct nl_pstate *npt) 251*2cef6288SAlexander V. Chernikov { 252*2cef6288SAlexander V. Chernikov int error; 253*2cef6288SAlexander V. Chernikov 254*2cef6288SAlexander V. Chernikov struct nl_parsed_state attrs = {}; 255*2cef6288SAlexander V. Chernikov error = nl_parse_nlmsg(hdr, &state_parser, npt, &attrs); 256*2cef6288SAlexander V. Chernikov if (error != 0) 257*2cef6288SAlexander V. Chernikov return (error); 258*2cef6288SAlexander V. Chernikov 259*2cef6288SAlexander V. Chernikov if (attrs.id != 0) 260*2cef6288SAlexander V. Chernikov error = handle_getstate(npt->nlp, &attrs, hdr, npt); 261*2cef6288SAlexander V. Chernikov else 262*2cef6288SAlexander V. Chernikov error = handle_dumpstates(npt->nlp, &attrs, hdr, npt); 263*2cef6288SAlexander V. Chernikov 264*2cef6288SAlexander V. Chernikov return (error); 265*2cef6288SAlexander V. Chernikov } 266*2cef6288SAlexander V. Chernikov 267*2cef6288SAlexander V. Chernikov static const struct nlhdr_parser *all_parsers[] = { &state_parser }; 268*2cef6288SAlexander V. Chernikov 269*2cef6288SAlexander V. Chernikov static int family_id; 270*2cef6288SAlexander V. Chernikov 271*2cef6288SAlexander V. Chernikov static const struct genl_cmd pf_cmds[] = { 272*2cef6288SAlexander V. Chernikov { 273*2cef6288SAlexander V. Chernikov .cmd_num = PFNL_CMD_GETSTATES, 274*2cef6288SAlexander V. Chernikov .cmd_name = "GETSTATES", 275*2cef6288SAlexander V. Chernikov .cmd_cb = pf_handle_getstates, 276*2cef6288SAlexander V. Chernikov .cmd_flags = GENL_CMD_CAP_DO | GENL_CMD_CAP_DUMP | GENL_CMD_CAP_HASPOL, 277*2cef6288SAlexander V. Chernikov }, 278*2cef6288SAlexander V. Chernikov }; 279*2cef6288SAlexander V. Chernikov 280*2cef6288SAlexander V. Chernikov void 281*2cef6288SAlexander V. Chernikov pf_nl_register(void) 282*2cef6288SAlexander V. Chernikov { 283*2cef6288SAlexander V. Chernikov NL_VERIFY_PARSERS(all_parsers); 284*2cef6288SAlexander V. Chernikov family_id = genl_register_family(PFNL_FAMILY_NAME, 0, 2, PFNL_CMD_MAX); 285*2cef6288SAlexander V. Chernikov genl_register_cmds(PFNL_FAMILY_NAME, pf_cmds, NL_ARRAY_LEN(pf_cmds)); 286*2cef6288SAlexander V. Chernikov } 287*2cef6288SAlexander V. Chernikov 288*2cef6288SAlexander V. Chernikov void 289*2cef6288SAlexander V. Chernikov pf_nl_unregister(void) 290*2cef6288SAlexander V. Chernikov { 291*2cef6288SAlexander V. Chernikov genl_unregister_family(PFNL_FAMILY_NAME); 292*2cef6288SAlexander V. Chernikov } 293