/**************************************************************************** * 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 #include #include #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; }