pfctl: Use the new DIOCGETRULENV ioctl
Create wrapper functions to handle the parsing of the nvlist and move that code into pfctl_ioctl.c. At some point this should be moved into a libpfctl. MFC after: 4 weeks Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D29560
This commit is contained in:
parent
d710367d11
commit
0d6c8174ef
@ -9,7 +9,7 @@ MAN= pfctl.8
|
||||
|
||||
SRCS = pfctl.c parse.y pfctl_parser.c pf_print_state.c pfctl_altq.c
|
||||
SRCS+= pfctl_osfp.c pfctl_radix.c pfctl_table.c pfctl_qstats.c
|
||||
SRCS+= pfctl_optimize.c
|
||||
SRCS+= pfctl_optimize.c pfctl_ioctl.c
|
||||
SRCS+= pf_ruleset.c
|
||||
|
||||
WARNS?= 2
|
||||
|
@ -63,6 +63,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "pfctl_ioctl.h"
|
||||
#include "pfctl_parser.h"
|
||||
#include "pfctl.h"
|
||||
|
||||
@ -952,8 +953,9 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
|
||||
|
||||
for (nr = 0; nr < mnr; ++nr) {
|
||||
pr.nr = nr;
|
||||
if (ioctl(dev, DIOCGETRULE, &pr)) {
|
||||
warn("DIOCGETRULE");
|
||||
if (pfctl_get_rule(dev, nr, pr.ticket, path, PF_SCRUB,
|
||||
&pr.rule, pr.anchor_call)) {
|
||||
warn("DIOCGETRULENV");
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -984,7 +986,8 @@ pfctl_show_rules(int dev, char *path, int opts, enum pfctl_show format,
|
||||
mnr = pr.nr;
|
||||
for (nr = 0; nr < mnr; ++nr) {
|
||||
pr.nr = nr;
|
||||
if (ioctl(dev, DIOCGETRULE, &pr)) {
|
||||
if (pfctl_get_rule(dev, nr, pr.ticket, path, PF_PASS,
|
||||
&pr.rule, pr.anchor_call)) {
|
||||
warn("DIOCGETRULE");
|
||||
goto error;
|
||||
}
|
||||
@ -1074,7 +1077,8 @@ pfctl_show_nat(int dev, int opts, char *anchorname)
|
||||
mnr = pr.nr;
|
||||
for (nr = 0; nr < mnr; ++nr) {
|
||||
pr.nr = nr;
|
||||
if (ioctl(dev, DIOCGETRULE, &pr)) {
|
||||
if (pfctl_get_rule(dev, nr, pr.ticket, anchorname,
|
||||
nattype[i], &pr.rule, pr.anchor_call)) {
|
||||
warn("DIOCGETRULE");
|
||||
return (-1);
|
||||
}
|
||||
|
339
sbin/pfctl/pfctl_ioctl.c
Normal file
339
sbin/pfctl/pfctl_ioctl.c
Normal file
@ -0,0 +1,339 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/nv.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/pfvar.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pfctl_ioctl.h"
|
||||
|
||||
static void
|
||||
pf_nvuint_8_array(const nvlist_t *nvl, const char *name, size_t maxelems,
|
||||
u_int8_t *numbers, size_t *nelems)
|
||||
{
|
||||
const uint64_t *tmp;
|
||||
size_t elems;
|
||||
|
||||
tmp = nvlist_get_number_array(nvl, name, &elems);
|
||||
assert(elems <= maxelems);
|
||||
|
||||
for (size_t i = 0; i < elems; i++)
|
||||
numbers[i] = tmp[i];
|
||||
|
||||
if (nelems)
|
||||
*nelems = elems;
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvuint_16_array(const nvlist_t *nvl, const char *name, size_t maxelems,
|
||||
u_int16_t *numbers, size_t *nelems)
|
||||
{
|
||||
const uint64_t *tmp;
|
||||
size_t elems;
|
||||
|
||||
tmp = nvlist_get_number_array(nvl, name, &elems);
|
||||
assert(elems <= maxelems);
|
||||
|
||||
for (size_t i = 0; i < elems; i++)
|
||||
numbers[i] = tmp[i];
|
||||
|
||||
if (nelems)
|
||||
*nelems = elems;
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvuint_32_array(const nvlist_t *nvl, const char *name, size_t maxelems,
|
||||
u_int32_t *numbers, size_t *nelems)
|
||||
{
|
||||
const uint64_t *tmp;
|
||||
size_t elems;
|
||||
|
||||
tmp = nvlist_get_number_array(nvl, name, &elems);
|
||||
assert(elems <= maxelems);
|
||||
|
||||
for (size_t i = 0; i < elems; i++)
|
||||
numbers[i] = tmp[i];
|
||||
|
||||
if (nelems)
|
||||
*nelems = elems;
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvuint_64_array(const nvlist_t *nvl, const char *name, size_t maxelems,
|
||||
u_int64_t *numbers, size_t *nelems)
|
||||
{
|
||||
const uint64_t *tmp;
|
||||
size_t elems;
|
||||
|
||||
tmp = nvlist_get_number_array(nvl, name, &elems);
|
||||
assert(elems <= maxelems);
|
||||
|
||||
for (size_t i = 0; i < elems; i++)
|
||||
numbers[i] = tmp[i];
|
||||
|
||||
if (nelems)
|
||||
*nelems = elems;
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvaddr_to_addr(const nvlist_t *nvl, struct pf_addr *addr)
|
||||
{
|
||||
size_t len;
|
||||
const void *data;
|
||||
|
||||
data = nvlist_get_binary(nvl, "addr", &len);
|
||||
assert(len == sizeof(struct pf_addr));
|
||||
memcpy(addr, data, len);
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvaddr_wrap_to_addr_wrap(const nvlist_t *nvl, struct pf_addr_wrap *addr)
|
||||
{
|
||||
addr->type = nvlist_get_number(nvl, "type");
|
||||
addr->iflags = nvlist_get_number(nvl, "iflags");
|
||||
strlcpy(addr->v.ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
|
||||
strlcpy(addr->v.tblname, nvlist_get_string(nvl, "tblname"),
|
||||
PF_TABLE_NAME_SIZE);
|
||||
|
||||
pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &addr->v.a.addr);
|
||||
pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "mask"), &addr->v.a.mask);
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
|
||||
{
|
||||
pf_nvaddr_wrap_to_addr_wrap(nvlist_get_nvlist(nvl, "addr"), &addr->addr);
|
||||
|
||||
pf_nvuint_16_array(nvl, "port", 2, addr->port, NULL);
|
||||
addr->neg = nvlist_get_number(nvl, "neg");
|
||||
addr->port_op = nvlist_get_number(nvl, "port_op");
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_pool *pool)
|
||||
{
|
||||
size_t len;
|
||||
const void *data;
|
||||
|
||||
data = nvlist_get_binary(nvl, "key", &len);
|
||||
assert(len == sizeof(pool->key));
|
||||
memcpy(&pool->key, data, len);
|
||||
|
||||
pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "counter"), &pool->counter);
|
||||
|
||||
pool->tblidx = nvlist_get_number(nvl, "tblidx");
|
||||
pf_nvuint_16_array(nvl, "proxy_port", 2, pool->proxy_port, NULL);
|
||||
pool->opts = nvlist_get_number(nvl, "opts");
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvrule_uid_to_rule_uid(const nvlist_t *nvl, struct pf_rule_uid *uid)
|
||||
{
|
||||
pf_nvuint_32_array(nvl, "uid", 2, uid->uid, NULL);
|
||||
uid->op = nvlist_get_number(nvl, "op");
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvdivert_to_divert(const nvlist_t *nvl, struct pf_rule *rule)
|
||||
{
|
||||
pf_nvaddr_to_addr(nvlist_get_nvlist(nvl, "addr"), &rule->divert.addr);
|
||||
rule->divert.port = nvlist_get_number(nvl, "port");
|
||||
}
|
||||
|
||||
static void
|
||||
pf_nvrule_to_rule(const nvlist_t *nvl, struct pf_rule *rule)
|
||||
{
|
||||
const uint64_t *skip;
|
||||
size_t skipcount;
|
||||
|
||||
rule->nr = nvlist_get_number(nvl, "nr");
|
||||
|
||||
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "src"), &rule->src);
|
||||
pf_nvrule_addr_to_rule_addr(nvlist_get_nvlist(nvl, "dst"), &rule->dst);
|
||||
|
||||
skip = nvlist_get_number_array(nvl, "skip", &skipcount);
|
||||
assert(skip);
|
||||
assert(skipcount == PF_SKIP_COUNT);
|
||||
for (int i = 0; i < PF_SKIP_COUNT; i++)
|
||||
rule->skip[i].nr = skip[i];
|
||||
|
||||
strlcpy(rule->label, nvlist_get_string(nvl, "label"), PF_RULE_LABEL_SIZE);
|
||||
strlcpy(rule->ifname, nvlist_get_string(nvl, "ifname"), IFNAMSIZ);
|
||||
strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE);
|
||||
strlcpy(rule->pqname, nvlist_get_string(nvl, "pqname"), PF_QNAME_SIZE);
|
||||
strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
|
||||
PF_TAG_NAME_SIZE);
|
||||
strlcpy(rule->match_tagname, nvlist_get_string(nvl, "match_tagname"),
|
||||
PF_TAG_NAME_SIZE);
|
||||
|
||||
strlcpy(rule->overload_tblname, nvlist_get_string(nvl, "overload_tblname"),
|
||||
PF_TABLE_NAME_SIZE);
|
||||
|
||||
pf_nvpool_to_pool(nvlist_get_nvlist(nvl, "rpool"), &rule->rpool);
|
||||
|
||||
rule->evaluations = nvlist_get_number(nvl, "evaluations");
|
||||
pf_nvuint_64_array(nvl, "packets", 2, rule->packets, NULL);
|
||||
pf_nvuint_64_array(nvl, "bytes", 2, rule->bytes, NULL);
|
||||
|
||||
rule->os_fingerprint = nvlist_get_number(nvl, "os_fingerprint");
|
||||
|
||||
rule->rtableid = nvlist_get_number(nvl, "rtableid");
|
||||
pf_nvuint_32_array(nvl, "timeout", PFTM_MAX, rule->timeout, NULL);
|
||||
rule->max_states = nvlist_get_number(nvl, "max_states");
|
||||
rule->max_src_nodes = nvlist_get_number(nvl, "max_src_nodes");
|
||||
rule->max_src_states = nvlist_get_number(nvl, "max_src_states");
|
||||
rule->max_src_conn = nvlist_get_number(nvl, "max_src_conn");
|
||||
rule->max_src_conn_rate.limit =
|
||||
nvlist_get_number(nvl, "max_src_conn_rate.limit");
|
||||
rule->max_src_conn_rate.seconds =
|
||||
nvlist_get_number(nvl, "max_src_conn_rate.seconds");
|
||||
rule->qid = nvlist_get_number(nvl, "qid");
|
||||
rule->pqid = nvlist_get_number(nvl, "pqid");
|
||||
rule->prob = nvlist_get_number(nvl, "prob");
|
||||
rule->cuid = nvlist_get_number(nvl, "cuid");
|
||||
rule->cpid = nvlist_get_number(nvl, "cpid");
|
||||
|
||||
rule->return_icmp = nvlist_get_number(nvl, "return_icmp");
|
||||
rule->return_icmp6 = nvlist_get_number(nvl, "return_icmp6");
|
||||
rule->max_mss = nvlist_get_number(nvl, "max_mss");
|
||||
rule->scrub_flags = nvlist_get_number(nvl, "scrub_flags");
|
||||
|
||||
pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "uid"), &rule->uid);
|
||||
pf_nvrule_uid_to_rule_uid(nvlist_get_nvlist(nvl, "gid"),
|
||||
(struct pf_rule_uid *)&rule->gid);
|
||||
|
||||
rule->rule_flag = nvlist_get_number(nvl, "rule_flag");
|
||||
rule->action = nvlist_get_number(nvl, "action");
|
||||
rule->direction = nvlist_get_number(nvl, "direction");
|
||||
rule->log = nvlist_get_number(nvl, "log");
|
||||
rule->logif = nvlist_get_number(nvl, "logif");
|
||||
rule->quick = nvlist_get_number(nvl, "quick");
|
||||
rule->ifnot = nvlist_get_number(nvl, "ifnot");
|
||||
rule->match_tag_not = nvlist_get_number(nvl, "match_tag_not");
|
||||
rule->natpass = nvlist_get_number(nvl, "natpass");
|
||||
|
||||
rule->keep_state = nvlist_get_number(nvl, "keep_state");
|
||||
rule->af = nvlist_get_number(nvl, "af");
|
||||
rule->proto = nvlist_get_number(nvl, "proto");
|
||||
rule->type = nvlist_get_number(nvl, "type");
|
||||
rule->code = nvlist_get_number(nvl, "code");
|
||||
rule->flags = nvlist_get_number(nvl, "flags");
|
||||
rule->flagset = nvlist_get_number(nvl, "flagset");
|
||||
rule->min_ttl = nvlist_get_number(nvl, "min_ttl");
|
||||
rule->allow_opts = nvlist_get_number(nvl, "allow_opts");
|
||||
rule->rt = nvlist_get_number(nvl, "rt");
|
||||
rule->return_ttl = nvlist_get_number(nvl, "return_ttl");
|
||||
rule->tos = nvlist_get_number(nvl, "tos");
|
||||
rule->set_tos = nvlist_get_number(nvl, "set_tos");
|
||||
rule->anchor_relative = nvlist_get_number(nvl, "anchor_relative");
|
||||
rule->anchor_wildcard = nvlist_get_number(nvl, "anchor_wildcard");
|
||||
|
||||
rule->flush = nvlist_get_number(nvl, "flush");
|
||||
rule->prio = nvlist_get_number(nvl, "prio");
|
||||
pf_nvuint_8_array(nvl, "set_prio", 2, rule->set_prio, NULL);
|
||||
|
||||
pf_nvdivert_to_divert(nvlist_get_nvlist(nvl, "divert"), rule);
|
||||
|
||||
rule->u_states_cur = nvlist_get_number(nvl, "states_cur");
|
||||
rule->u_states_tot = nvlist_get_number(nvl, "states_tot");
|
||||
rule->u_src_nodes = nvlist_get_number(nvl, "src_nodes");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket, const char *anchor,
|
||||
u_int32_t ruleset, struct pf_rule *rule, char *anchor_call)
|
||||
{
|
||||
struct pfioc_nv nv;
|
||||
nvlist_t *nvl;
|
||||
void *nvlpacked;
|
||||
int ret;
|
||||
|
||||
nvl = nvlist_create(0);
|
||||
if (nvl == 0)
|
||||
return (ENOMEM);
|
||||
|
||||
nvlist_add_number(nvl, "nr", nr);
|
||||
nvlist_add_number(nvl, "ticket", ticket);
|
||||
nvlist_add_string(nvl, "anchor", anchor);
|
||||
nvlist_add_number(nvl, "ruleset", ruleset);
|
||||
|
||||
nvlpacked = nvlist_pack(nvl, &nv.len);
|
||||
if (nvlpacked == NULL) {
|
||||
nvlist_destroy(nvl);
|
||||
return (ENOMEM);
|
||||
}
|
||||
nv.data = malloc(8182);
|
||||
nv.size = 8192;
|
||||
assert(nv.len <= nv.size);
|
||||
memcpy(nv.data, nvlpacked, nv.len);
|
||||
nvlist_destroy(nvl);
|
||||
nvl = NULL;
|
||||
free(nvlpacked);
|
||||
|
||||
ret = ioctl(dev, DIOCGETRULENV, &nv);
|
||||
if (ret != 0) {
|
||||
free(nv.data);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
nvl = nvlist_unpack(nv.data, nv.len, 0);
|
||||
if (nvl == NULL) {
|
||||
free(nv.data);
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
pf_nvrule_to_rule(nvlist_get_nvlist(nvl, "rule"), rule);
|
||||
|
||||
if (anchor_call)
|
||||
strlcpy(anchor_call, nvlist_get_string(nvl, "anchor_call"),
|
||||
MAXPATHLEN);
|
||||
|
||||
free(nv.data);
|
||||
nvlist_destroy(nvl);
|
||||
|
||||
return (0);
|
||||
}
|
43
sbin/pfctl/pfctl_ioctl.h
Normal file
43
sbin/pfctl/pfctl_ioctl.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _PFCTL_IOCTL_H_
|
||||
#define _PFCTL_IOCTL_H_
|
||||
|
||||
#include <netpfil/pf/pf.h>
|
||||
|
||||
int pfctl_get_rule(int dev, u_int32_t nr, u_int32_t ticket,
|
||||
const char *anchor, u_int32_t ruleset, struct pf_rule *rule,
|
||||
char *anchor_call);
|
||||
|
||||
#endif
|
@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pfctl_ioctl.h"
|
||||
#include "pfctl_parser.h"
|
||||
#include "pfctl.h"
|
||||
|
||||
@ -909,8 +910,10 @@ load_feedback_profile(struct pfctl *pf, struct superblocks *superblocks)
|
||||
return (1);
|
||||
}
|
||||
pr.nr = nr;
|
||||
if (ioctl(pf->dev, DIOCGETRULE, &pr)) {
|
||||
warn("DIOCGETRULES");
|
||||
|
||||
if (pfctl_get_rule(pf->dev, nr, pr.ticket, "", PF_PASS,
|
||||
&pr.rule, pr.anchor_call)) {
|
||||
warn("DIOCGETRULENV");
|
||||
return (1);
|
||||
}
|
||||
memcpy(&por->por_rule, &pr.rule, sizeof(por->por_rule));
|
||||
|
Loading…
Reference in New Issue
Block a user