Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 
0003 #include <errno.h>
0004 #include <stdbool.h>
0005 #include <stdio.h>
0006 #include <stdint.h>
0007 #include <stdlib.h>
0008 #include <unistd.h>
0009 #include <string.h>
0010 #include <time.h>
0011 #include <arpa/inet.h>
0012 
0013 #include <libmnl/libmnl.h>
0014 #include <linux/netfilter.h>
0015 #include <linux/netfilter/nfnetlink.h>
0016 #include <linux/netfilter/nfnetlink_queue.h>
0017 
0018 struct options {
0019     bool count_packets;
0020     bool gso_enabled;
0021     int verbose;
0022     unsigned int queue_num;
0023     unsigned int timeout;
0024     uint32_t verdict;
0025     uint32_t delay_ms;
0026 };
0027 
0028 static unsigned int queue_stats[5];
0029 static struct options opts;
0030 
0031 static void help(const char *p)
0032 {
0033     printf("Usage: %s [-c|-v [-vv] ] [-t timeout] [-q queue_num] [-Qdst_queue ] [ -d ms_delay ] [-G]\n", p);
0034 }
0035 
0036 static int parse_attr_cb(const struct nlattr *attr, void *data)
0037 {
0038     const struct nlattr **tb = data;
0039     int type = mnl_attr_get_type(attr);
0040 
0041     /* skip unsupported attribute in user-space */
0042     if (mnl_attr_type_valid(attr, NFQA_MAX) < 0)
0043         return MNL_CB_OK;
0044 
0045     switch (type) {
0046     case NFQA_MARK:
0047     case NFQA_IFINDEX_INDEV:
0048     case NFQA_IFINDEX_OUTDEV:
0049     case NFQA_IFINDEX_PHYSINDEV:
0050     case NFQA_IFINDEX_PHYSOUTDEV:
0051         if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
0052             perror("mnl_attr_validate");
0053             return MNL_CB_ERROR;
0054         }
0055         break;
0056     case NFQA_TIMESTAMP:
0057         if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
0058             sizeof(struct nfqnl_msg_packet_timestamp)) < 0) {
0059             perror("mnl_attr_validate2");
0060             return MNL_CB_ERROR;
0061         }
0062         break;
0063     case NFQA_HWADDR:
0064         if (mnl_attr_validate2(attr, MNL_TYPE_UNSPEC,
0065             sizeof(struct nfqnl_msg_packet_hw)) < 0) {
0066             perror("mnl_attr_validate2");
0067             return MNL_CB_ERROR;
0068         }
0069         break;
0070     case NFQA_PAYLOAD:
0071         break;
0072     }
0073     tb[type] = attr;
0074     return MNL_CB_OK;
0075 }
0076 
0077 static int queue_cb(const struct nlmsghdr *nlh, void *data)
0078 {
0079     struct nlattr *tb[NFQA_MAX+1] = { 0 };
0080     struct nfqnl_msg_packet_hdr *ph = NULL;
0081     uint32_t id = 0;
0082 
0083     (void)data;
0084 
0085     mnl_attr_parse(nlh, sizeof(struct nfgenmsg), parse_attr_cb, tb);
0086     if (tb[NFQA_PACKET_HDR]) {
0087         ph = mnl_attr_get_payload(tb[NFQA_PACKET_HDR]);
0088         id = ntohl(ph->packet_id);
0089 
0090         if (opts.verbose > 0)
0091             printf("packet hook=%u, hwproto 0x%x",
0092                 ntohs(ph->hw_protocol), ph->hook);
0093 
0094         if (ph->hook >= 5) {
0095             fprintf(stderr, "Unknown hook %d\n", ph->hook);
0096             return MNL_CB_ERROR;
0097         }
0098 
0099         if (opts.verbose > 0) {
0100             uint32_t skbinfo = 0;
0101 
0102             if (tb[NFQA_SKB_INFO])
0103                 skbinfo = ntohl(mnl_attr_get_u32(tb[NFQA_SKB_INFO]));
0104             if (skbinfo & NFQA_SKB_CSUMNOTREADY)
0105                 printf(" csumnotready");
0106             if (skbinfo & NFQA_SKB_GSO)
0107                 printf(" gso");
0108             if (skbinfo & NFQA_SKB_CSUM_NOTVERIFIED)
0109                 printf(" csumnotverified");
0110             puts("");
0111         }
0112 
0113         if (opts.count_packets)
0114             queue_stats[ph->hook]++;
0115     }
0116 
0117     return MNL_CB_OK + id;
0118 }
0119 
0120 static struct nlmsghdr *
0121 nfq_build_cfg_request(char *buf, uint8_t command, int queue_num)
0122 {
0123     struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
0124     struct nfqnl_msg_config_cmd cmd = {
0125         .command = command,
0126         .pf = htons(AF_INET),
0127     };
0128     struct nfgenmsg *nfg;
0129 
0130     nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
0131     nlh->nlmsg_flags = NLM_F_REQUEST;
0132 
0133     nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
0134 
0135     nfg->nfgen_family = AF_UNSPEC;
0136     nfg->version = NFNETLINK_V0;
0137     nfg->res_id = htons(queue_num);
0138 
0139     mnl_attr_put(nlh, NFQA_CFG_CMD, sizeof(cmd), &cmd);
0140 
0141     return nlh;
0142 }
0143 
0144 static struct nlmsghdr *
0145 nfq_build_cfg_params(char *buf, uint8_t mode, int range, int queue_num)
0146 {
0147     struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
0148     struct nfqnl_msg_config_params params = {
0149         .copy_range = htonl(range),
0150         .copy_mode = mode,
0151     };
0152     struct nfgenmsg *nfg;
0153 
0154     nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_CONFIG;
0155     nlh->nlmsg_flags = NLM_F_REQUEST;
0156 
0157     nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
0158     nfg->nfgen_family = AF_UNSPEC;
0159     nfg->version = NFNETLINK_V0;
0160     nfg->res_id = htons(queue_num);
0161 
0162     mnl_attr_put(nlh, NFQA_CFG_PARAMS, sizeof(params), &params);
0163 
0164     return nlh;
0165 }
0166 
0167 static struct nlmsghdr *
0168 nfq_build_verdict(char *buf, int id, int queue_num, uint32_t verd)
0169 {
0170     struct nfqnl_msg_verdict_hdr vh = {
0171         .verdict = htonl(verd),
0172         .id = htonl(id),
0173     };
0174     struct nlmsghdr *nlh;
0175     struct nfgenmsg *nfg;
0176 
0177     nlh = mnl_nlmsg_put_header(buf);
0178     nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | NFQNL_MSG_VERDICT;
0179     nlh->nlmsg_flags = NLM_F_REQUEST;
0180     nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg));
0181     nfg->nfgen_family = AF_UNSPEC;
0182     nfg->version = NFNETLINK_V0;
0183     nfg->res_id = htons(queue_num);
0184 
0185     mnl_attr_put(nlh, NFQA_VERDICT_HDR, sizeof(vh), &vh);
0186 
0187     return nlh;
0188 }
0189 
0190 static void print_stats(void)
0191 {
0192     unsigned int last, total;
0193     int i;
0194 
0195     total = 0;
0196     last = queue_stats[0];
0197 
0198     for (i = 0; i < 5; i++) {
0199         printf("hook %d packets %08u\n", i, queue_stats[i]);
0200         last = queue_stats[i];
0201         total += last;
0202     }
0203 
0204     printf("%u packets total\n", total);
0205 }
0206 
0207 struct mnl_socket *open_queue(void)
0208 {
0209     char buf[MNL_SOCKET_BUFFER_SIZE];
0210     unsigned int queue_num;
0211     struct mnl_socket *nl;
0212     struct nlmsghdr *nlh;
0213     struct timeval tv;
0214     uint32_t flags;
0215 
0216     nl = mnl_socket_open(NETLINK_NETFILTER);
0217     if (nl == NULL) {
0218         perror("mnl_socket_open");
0219         exit(EXIT_FAILURE);
0220     }
0221 
0222     if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
0223         perror("mnl_socket_bind");
0224         exit(EXIT_FAILURE);
0225     }
0226 
0227     queue_num = opts.queue_num;
0228     nlh = nfq_build_cfg_request(buf, NFQNL_CFG_CMD_BIND, queue_num);
0229 
0230     if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
0231         perror("mnl_socket_sendto");
0232         exit(EXIT_FAILURE);
0233     }
0234 
0235     nlh = nfq_build_cfg_params(buf, NFQNL_COPY_PACKET, 0xFFFF, queue_num);
0236 
0237     flags = opts.gso_enabled ? NFQA_CFG_F_GSO : 0;
0238     flags |= NFQA_CFG_F_UID_GID;
0239     mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(flags));
0240     mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(flags));
0241 
0242     if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
0243         perror("mnl_socket_sendto");
0244         exit(EXIT_FAILURE);
0245     }
0246 
0247     memset(&tv, 0, sizeof(tv));
0248     tv.tv_sec = opts.timeout;
0249     if (opts.timeout && setsockopt(mnl_socket_get_fd(nl),
0250                        SOL_SOCKET, SO_RCVTIMEO,
0251                        &tv, sizeof(tv))) {
0252         perror("setsockopt(SO_RCVTIMEO)");
0253         exit(EXIT_FAILURE);
0254     }
0255 
0256     return nl;
0257 }
0258 
0259 static void sleep_ms(uint32_t delay)
0260 {
0261     struct timespec ts = { .tv_sec = delay / 1000 };
0262 
0263     delay %= 1000;
0264 
0265     ts.tv_nsec = delay * 1000llu * 1000llu;
0266 
0267     nanosleep(&ts, NULL);
0268 }
0269 
0270 static int mainloop(void)
0271 {
0272     unsigned int buflen = 64 * 1024 + MNL_SOCKET_BUFFER_SIZE;
0273     struct mnl_socket *nl;
0274     struct nlmsghdr *nlh;
0275     unsigned int portid;
0276     char *buf;
0277     int ret;
0278 
0279     buf = malloc(buflen);
0280     if (!buf) {
0281         perror("malloc");
0282         exit(EXIT_FAILURE);
0283     }
0284 
0285     nl = open_queue();
0286     portid = mnl_socket_get_portid(nl);
0287 
0288     for (;;) {
0289         uint32_t id;
0290 
0291         ret = mnl_socket_recvfrom(nl, buf, buflen);
0292         if (ret == -1) {
0293             if (errno == ENOBUFS || errno == EINTR)
0294                 continue;
0295 
0296             if (errno == EAGAIN) {
0297                 errno = 0;
0298                 ret = 0;
0299                 break;
0300             }
0301 
0302             perror("mnl_socket_recvfrom");
0303             exit(EXIT_FAILURE);
0304         }
0305 
0306         ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, NULL);
0307         if (ret < 0) {
0308             perror("mnl_cb_run");
0309             exit(EXIT_FAILURE);
0310         }
0311 
0312         id = ret - MNL_CB_OK;
0313         if (opts.delay_ms)
0314             sleep_ms(opts.delay_ms);
0315 
0316         nlh = nfq_build_verdict(buf, id, opts.queue_num, opts.verdict);
0317         if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
0318             perror("mnl_socket_sendto");
0319             exit(EXIT_FAILURE);
0320         }
0321     }
0322 
0323     mnl_socket_close(nl);
0324 
0325     return ret;
0326 }
0327 
0328 static void parse_opts(int argc, char **argv)
0329 {
0330     int c;
0331 
0332     while ((c = getopt(argc, argv, "chvt:q:Q:d:G")) != -1) {
0333         switch (c) {
0334         case 'c':
0335             opts.count_packets = true;
0336             break;
0337         case 'h':
0338             help(argv[0]);
0339             exit(0);
0340             break;
0341         case 'q':
0342             opts.queue_num = atoi(optarg);
0343             if (opts.queue_num > 0xffff)
0344                 opts.queue_num = 0;
0345             break;
0346         case 'Q':
0347             opts.verdict = atoi(optarg);
0348             if (opts.verdict > 0xffff) {
0349                 fprintf(stderr, "Expected destination queue number\n");
0350                 exit(1);
0351             }
0352 
0353             opts.verdict <<= 16;
0354             opts.verdict |= NF_QUEUE;
0355             break;
0356         case 'd':
0357             opts.delay_ms = atoi(optarg);
0358             if (opts.delay_ms == 0) {
0359                 fprintf(stderr, "Expected nonzero delay (in milliseconds)\n");
0360                 exit(1);
0361             }
0362             break;
0363         case 't':
0364             opts.timeout = atoi(optarg);
0365             break;
0366         case 'G':
0367             opts.gso_enabled = false;
0368             break;
0369         case 'v':
0370             opts.verbose++;
0371             break;
0372         }
0373     }
0374 
0375     if (opts.verdict != NF_ACCEPT && (opts.verdict >> 16 == opts.queue_num)) {
0376         fprintf(stderr, "Cannot use same destination and source queue\n");
0377         exit(1);
0378     }
0379 }
0380 
0381 int main(int argc, char *argv[])
0382 {
0383     int ret;
0384 
0385     opts.verdict = NF_ACCEPT;
0386     opts.gso_enabled = true;
0387 
0388     parse_opts(argc, argv);
0389 
0390     ret = mainloop();
0391     if (opts.count_packets)
0392         print_stats();
0393 
0394     return ret;
0395 }