This is an automated email from the ASF dual-hosted git repository. xiaoxiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 7e7b108ef9509176abcd03daf62a5e84a4d5c72d Author: Zhe Weng <[email protected]> AuthorDate: Mon Mar 25 16:36:30 2024 +0800 netutils/netlib: Add netfilter conntrack functions Signed-off-by: Zhe Weng <[email protected]> --- include/netutils/netlib.h | 64 +++++++ netutils/netlib/CMakeLists.txt | 6 + netutils/netlib/Makefile | 6 + netutils/netlib/netlib_conntrack.c | 348 +++++++++++++++++++++++++++++++++++++ 4 files changed, 424 insertions(+) diff --git a/include/netutils/netlib.h b/include/netutils/netlib.h index e417f11a6..ba6bb618b 100644 --- a/include/netutils/netlib.h +++ b/include/netutils/netlib.h @@ -123,6 +123,60 @@ struct netlib_device_s }; #endif /* CONFIG_NETLINK_ROUTE*/ +#ifdef CONFIG_NETLINK_NETFILTER +/* Describes one connection returned by netlib_get_conntrack() */ + +union netlib_conntrack_addr_u +{ +#ifdef CONFIG_NET_IPv4 + struct in_addr ipv4; +#endif +#ifdef CONFIG_NET_IPv6 + struct in6_addr ipv6; +#endif +}; + +struct netlib_conntrack_tuple_s +{ + union netlib_conntrack_addr_u src; + union netlib_conntrack_addr_u dst; + + union + { + struct + { + uint16_t sport; + uint16_t dport; + } tcp; /* and udp */ + + struct + { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; /* and icmp6 */ + } l4; + + uint8_t l4proto; +}; + +struct netlib_conntrack_s +{ + struct netlib_conntrack_tuple_s orig; + struct netlib_conntrack_tuple_s reply; + + sa_family_t family; /* AF_INET or AF_INET6 */ + uint8_t type; /* IPCTNL_MSG_CT_* */ +}; + +/* There might be many conntrack entries, so we don't use array of data, but + * use callback instead. + */ + +typedef CODE int (*netlib_conntrack_cb_t)(FAR struct netlib_conntrack_s *ct); + +#endif /* CONFIG_NETLINK_NETFILTER */ + #ifdef CONFIG_NETUTILS_NETLIB_GENERICURLPARSER struct url_s { @@ -355,6 +409,16 @@ FAR struct ipt_entry *netlib_ipt_masquerade_entry(FAR const char *ifname); # endif #endif +#ifdef CONFIG_NETLINK_NETFILTER +/* Netfilter connection tracking support */ + +struct nlmsghdr; /* Forward reference */ + +int netlib_parse_conntrack(FAR const struct nlmsghdr *nlh, size_t len, + FAR struct netlib_conntrack_s *ct); +int netlib_get_conntrack(sa_family_t family, netlib_conntrack_cb_t cb); +#endif + /* HTTP support */ int netlib_parsehttpurl(FAR const char *url, uint16_t *port, diff --git a/netutils/netlib/CMakeLists.txt b/netutils/netlib/CMakeLists.txt index fd039bc53..e3bd0cb8b 100644 --- a/netutils/netlib/CMakeLists.txt +++ b/netutils/netlib/CMakeLists.txt @@ -84,6 +84,12 @@ if(CONFIG_NETUTILS_NETLIB) endif() endif() + # Netfilter connection support + + if(CONFIG_NETLINK_NETFILTER) + list(APPEND SRCS netlib_conntrack.c) + endif() + # These require TCP support if(CONFIG_NET_TCP) diff --git a/netutils/netlib/Makefile b/netutils/netlib/Makefile index 99bb54bd0..9b0525924 100644 --- a/netutils/netlib/Makefile +++ b/netutils/netlib/Makefile @@ -85,6 +85,12 @@ CSRCS += netlib_getroute.c endif endif +# Netfilter connection support + +ifeq ($(CONFIG_NETLINK_NETFILTER),y) +CSRCS += netlib_conntrack.c +endif + # These require TCP support ifeq ($(CONFIG_NET_TCP),y) diff --git a/netutils/netlib/netlib_conntrack.c b/netutils/netlib/netlib_conntrack.c new file mode 100644 index 000000000..fda39476a --- /dev/null +++ b/netutils/netlib/netlib_conntrack.c @@ -0,0 +1,348 @@ +/**************************************************************************** + * apps/netutils/netlib/netlib_conntrack.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <netpacket/netlink.h> + +#include <nuttx/net/ip.h> + +#include "netutils/netlib.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define RXBUFFER_SIZE 256 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct netlib_conntrack_sendto_request_s +{ + struct nlmsghdr hdr; + struct nfgenmsg msg; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_ct_parse_ip + ****************************************************************************/ + +static void netlib_ct_parse_ip(FAR const struct nfattr *attr, + FAR struct netlib_conntrack_tuple_s *tuple) +{ + FAR const struct nfattr *subattr = NFA_DATA(attr); + ssize_t paylen = NFA_PAYLOAD(attr); + + for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen)) + { + switch (NFA_TYPE(subattr)) + { + case CTA_IP_V4_SRC: + net_ipv4addr_hdrcopy(&tuple->src.ipv4, NFA_DATA(subattr)); + break; + case CTA_IP_V4_DST: + net_ipv4addr_hdrcopy(&tuple->dst.ipv4, NFA_DATA(subattr)); + break; + case CTA_IP_V6_SRC: + net_ipv6addr_hdrcopy(&tuple->src.ipv6, NFA_DATA(subattr)); + break; + case CTA_IP_V6_DST: + net_ipv6addr_hdrcopy(&tuple->dst.ipv6, NFA_DATA(subattr)); + break; + } + } +} + +/**************************************************************************** + * Name: netlib_ct_parse_proto + ****************************************************************************/ + +static void netlib_ct_parse_proto(FAR const struct nfattr *attr, + FAR struct netlib_conntrack_tuple_s *tuple) +{ + FAR const struct nfattr *subattr = NFA_DATA(attr); + ssize_t paylen = NFA_PAYLOAD(attr); + + for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen)) + { + switch (NFA_TYPE(subattr)) + { + case CTA_PROTO_NUM: + tuple->l4proto = *(FAR uint8_t *)NFA_DATA(subattr); + break; + + case CTA_PROTO_SRC_PORT: + tuple->l4.tcp.sport = ntohs(*(FAR uint16_t *)NFA_DATA(subattr)); + break; + + case CTA_PROTO_DST_PORT: + tuple->l4.tcp.dport = ntohs(*(FAR uint16_t *)NFA_DATA(subattr)); + break; + + case CTA_PROTO_ICMP_ID: + case CTA_PROTO_ICMPV6_ID: + tuple->l4.icmp.id = ntohs(*(FAR uint16_t *)NFA_DATA(subattr)); + break; + + case CTA_PROTO_ICMP_TYPE: + case CTA_PROTO_ICMPV6_TYPE: + tuple->l4.icmp.type = *(FAR uint8_t *)NFA_DATA(subattr); + break; + + case CTA_PROTO_ICMP_CODE: + case CTA_PROTO_ICMPV6_CODE: + tuple->l4.icmp.code = *(FAR uint8_t *)NFA_DATA(subattr); + break; + } + } +} + +/**************************************************************************** + * Name: netlib_ct_parse_tuple + ****************************************************************************/ + +static void netlib_ct_parse_tuple(FAR const struct nfattr *attr, + FAR struct netlib_conntrack_tuple_s *tuple) +{ + FAR const struct nfattr *subattr = NFA_DATA(attr); + ssize_t paylen = NFA_PAYLOAD(attr); + + for (; NFA_OK(subattr, paylen); subattr = NFA_NEXT(subattr, paylen)) + { + switch (NFA_TYPE(subattr)) + { + case CTA_TUPLE_IP: + netlib_ct_parse_ip(subattr, tuple); + break; + + case CTA_TUPLE_PROTO: + netlib_ct_parse_proto(subattr, tuple); + break; + } + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: netlib_parse_conntrack + * + * Description: + * Parse a conntrack message. + * + * Input Parameters: + * nlh - The netlink message to parse. + * ct - The conntrack to fill. + * + * Returned Value: + * OK on success, -errno on failure. + * + ****************************************************************************/ + +int netlib_parse_conntrack(FAR const struct nlmsghdr *nlh, size_t len, + FAR struct netlib_conntrack_s *ct) +{ + FAR const struct nfgenmsg *nfmsg; + FAR const struct nfattr *attr; + ssize_t paylen; + + if (len < sizeof(struct nlmsghdr) || len < nlh->nlmsg_len) + { + fprintf(stderr, "Error message length got %zd vs %" PRIu32 "\n", + len, nlh->nlmsg_len); + return -EINVAL; + } + + if (NFNL_SUBSYS_ID(nlh->nlmsg_type) != NFNL_SUBSYS_CTNETLINK) + { + fprintf(stderr, "Unknown message type %" PRIx32 "\n", nlh->nlmsg_type); + return -ENOTSUP; + } + + nfmsg = NLMSG_DATA(nlh); + attr = NFM_NFA(nfmsg); + paylen = NFM_PAYLOAD(nlh); + + ct->family = nfmsg->nfgen_family; + ct->type = NFNL_MSG_TYPE(nlh->nlmsg_type); + + for (; NFA_OK(attr, paylen); attr = NFA_NEXT(attr, paylen)) + { + switch (NFA_TYPE(attr)) + { + case CTA_TUPLE_ORIG: + netlib_ct_parse_tuple(attr, &ct->orig); + break; + case CTA_TUPLE_REPLY: + netlib_ct_parse_tuple(attr, &ct->reply); + break; + } + } + + return OK; +} + +/**************************************************************************** + * Name: netlib_get_conntrack + * + * Description: + * Get the conntrack table. + * + * Input Parameters: + * family - The address family to get the conntrack table for. + * cb - The callback to call for each conntrack entry. + * + * Returned Value: + * The number of conntrack entries processed on success, -errno on failure. + * + ****************************************************************************/ + +int netlib_get_conntrack(sa_family_t family, netlib_conntrack_cb_t cb) +{ + FAR const struct nlmsghdr *nlh; + struct netlib_conntrack_sendto_request_s req; + struct netlib_conntrack_s ct; + struct sockaddr_nl addr; + static unsigned int seqno = 0; + unsigned int thiseq; + uint8_t buf[RXBUFFER_SIZE]; + ssize_t nrecvd; + size_t cnt; + int fd; + int ret; + + /* Create a NetLink socket with NETLINK_NETFILTER protocol */ + + fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NETFILTER); + if (fd < 0) + { + perror("ERROR: failed to create netlink socket"); + return -errno; + } + + addr.nl_family = AF_NETLINK; + addr.nl_pad = 0; + addr.nl_pid = getpid(); + addr.nl_groups = 0; + + if (bind(fd, (FAR const struct sockaddr *)&addr, sizeof(addr)) < 0) + { + perror("ERROR: failed to bind netlink socket"); + ret = -errno; + goto errout_with_socket; + } + + /* Initialize the request */ + + thiseq = ++seqno; + + memset(&req, 0, sizeof(req)); + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct nfgenmsg)); + req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.hdr.nlmsg_seq = thiseq; + req.hdr.nlmsg_type = NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_GET; + req.hdr.nlmsg_pid = addr.nl_pid; + req.msg.nfgen_family = family; + req.msg.version = NFNETLINK_V0; + req.msg.res_id = 0; + + ret = send(fd, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) + { + perror("ERROR: send() failed"); + ret = -errno; + goto errout_with_socket; + } + + /* Read the response */ + + for (cnt = 0; ; cnt++) + { + nrecvd = recv(fd, buf, sizeof(buf), 0); + if (nrecvd < 0) + { + perror("ERROR: recv() failed"); + ret = -errno; + goto errout_with_socket; + } + + nlh = (FAR struct nlmsghdr *)buf; + + /* Verify the data and transfer the connection info to the caller */ + + if (nrecvd < sizeof(struct nlmsghdr) || + nlh->nlmsg_len < sizeof(struct nlmsghdr)) + { + fprintf(stderr, "ERROR: Bad message\n"); + ret = -EIO; + goto errout_with_socket; + } + + /* The sequence number in the response should match the sequence + * number in the request (since we created the socket, this should + * always be true). + */ + + if (nlh->nlmsg_seq != thiseq) + { + fprintf(stderr, "ERROR: Bad sequence number in response\n"); + ret = -EIO; + goto errout_with_socket; + } + + /* Callback the connection info to the caller */ + + if (nlh->nlmsg_type == NLMSG_DONE) + { + ret = cnt; + break; + } + + ret = netlib_parse_conntrack(nlh, nrecvd, &ct); + if (ret < 0) + { + fprintf(stderr, "ERROR: failed to parse conntrack message\n"); + goto errout_with_socket; + } + + ret = cb(&ct); + if (ret < 0) + { + fprintf(stderr, "ERROR: callback failed\n"); + goto errout_with_socket; + } + } + +errout_with_socket: + close(fd); + return ret; +}
