libmnl  1.0.4
nfct-create-batch.c
1 /* This example is placed in the public domain. */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <arpa/inet.h>
6 #include <time.h>
7 #include <sys/select.h>
8 #include <string.h>
9 
10 #include <libmnl/libmnl.h>
11 #include <linux/netfilter/nfnetlink.h>
12 #include <linux/netfilter/nfnetlink_conntrack.h>
13 #include <linux/netfilter/nf_conntrack_common.h>
14 #include <linux/netfilter/nf_conntrack_tcp.h>
15 
16 static void put_msg(char *buf, uint16_t i, int seq)
17 {
18  struct nlmsghdr *nlh;
19  struct nfgenmsg *nfh;
20  struct nlattr *nest1, *nest2;
21 
22  nlh = mnl_nlmsg_put_header(buf);
23  nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
24  nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
25  nlh->nlmsg_seq = seq;
26 
27  nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
28  nfh->nfgen_family = AF_INET;
29  nfh->version = NFNETLINK_V0;
30  nfh->res_id = 0;
31 
32  nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_ORIG);
33  nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
34  mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("1.1.1.1"));
35  mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("2.2.2.2"));
36  mnl_attr_nest_end(nlh, nest2);
37 
38  nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
39  mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
40  mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(i));
41  mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(1025));
42  mnl_attr_nest_end(nlh, nest2);
43  mnl_attr_nest_end(nlh, nest1);
44 
45  nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_REPLY);
46  nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
47  mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("2.2.2.2"));
48  mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("1.1.1.1"));
49  mnl_attr_nest_end(nlh, nest2);
50 
51  nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
52  mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
53  mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(1025));
54  mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(i));
55  mnl_attr_nest_end(nlh, nest2);
56  mnl_attr_nest_end(nlh, nest1);
57 
58  nest1 = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
59  nest2 = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
60  mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_SYN_SENT);
61  mnl_attr_nest_end(nlh, nest2);
62  mnl_attr_nest_end(nlh, nest1);
63 
64  mnl_attr_put_u32(nlh, CTA_STATUS, htonl(IPS_CONFIRMED));
65  mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(1000));
66 }
67 
68 static int cb_err(const struct nlmsghdr *nlh, void *data)
69 {
70  struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
71  if (err->error != 0)
72  printf("message with seq %u has failed: %s\n",
73  nlh->nlmsg_seq, strerror(-err->error));
74  return MNL_CB_OK;
75 }
76 
77 static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = {
78  [NLMSG_ERROR] = cb_err,
79 };
80 
81 static void
82 send_batch(struct mnl_socket *nl, struct mnl_nlmsg_batch *b, int portid)
83 {
84  int ret, fd = mnl_socket_get_fd(nl);
85  size_t len = mnl_nlmsg_batch_size(b);
86  char rcv_buf[MNL_SOCKET_BUFFER_SIZE];
87 
88  ret = mnl_socket_sendto(nl, mnl_nlmsg_batch_head(b), len);
89  if (ret == -1) {
90  perror("mnl_socket_sendto");
91  exit(EXIT_FAILURE);
92  }
93 
94  /* receive and digest all the acknowledgments from the kernel. */
95  struct timeval tv = {
96  .tv_sec = 0,
97  .tv_usec = 0
98  };
99  fd_set readfds;
100  FD_ZERO(&readfds);
101  FD_SET(fd, &readfds);
102 
103  ret = select(fd+1, &readfds, NULL, NULL, &tv);
104  if (ret == -1) {
105  perror("select");
106  exit(EXIT_FAILURE);
107  }
108  while (ret > 0 && FD_ISSET(fd, &readfds)) {
109  ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf));
110  if (ret == -1) {
111  perror("mnl_socket_recvfrom");
112  exit(EXIT_FAILURE);
113  }
114 
115  ret = mnl_cb_run2(rcv_buf, ret, 0, portid,
116  NULL, NULL, cb_ctl_array,
117  MNL_ARRAY_SIZE(cb_ctl_array));
118  if (ret == -1) {
119  perror("mnl_cb_run2");
120  exit(EXIT_FAILURE);
121  }
122 
123  ret = select(fd+1, &readfds, NULL, NULL, &tv);
124  if (ret == -1) {
125  perror("select");
126  exit(EXIT_FAILURE);
127  }
128  FD_ZERO(&readfds);
129  FD_SET(fd, &readfds);
130  }
131 }
132 
133 int main(void)
134 {
135  struct mnl_socket *nl;
136  char snd_buf[MNL_SOCKET_BUFFER_SIZE*2];
137  struct mnl_nlmsg_batch *b;
138  int j;
139  unsigned int seq, portid;
140  uint16_t i;
141 
142  nl = mnl_socket_open(NETLINK_NETFILTER);
143  if (nl == NULL) {
144  perror("mnl_socket_open");
145  exit(EXIT_FAILURE);
146  }
147  if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
148  perror("mnl_socket_bind");
149  exit(EXIT_FAILURE);
150  }
151  portid = mnl_socket_get_portid(nl);
152 
153  /* The buffer that we use to batch messages is MNL_SOCKET_BUFFER_SIZE
154  * multiplied by 2 bytes long, but we limit the batch to half of it
155  * since the last message that does not fit the batch goes over the
156  * upper boundary, if you break this rule, expect memory corruptions. */
157  b = mnl_nlmsg_batch_start(snd_buf, MNL_SOCKET_BUFFER_SIZE);
158  if (b == NULL) {
159  perror("mnl_nlmsg_batch_start");
160  exit(EXIT_FAILURE);
161  }
162 
163  seq = time(NULL);
164  for (i=1024, j=0; i<65535; i++, j++) {
165  put_msg(mnl_nlmsg_batch_current(b), i, seq+j);
166 
167  /* is there room for more messages in this batch?
168  * if so, continue. */
169  if (mnl_nlmsg_batch_next(b))
170  continue;
171 
172  send_batch(nl, b, portid);
173 
174  /* this moves the last message that did not fit into the
175  * batch to the head of it. */
176  mnl_nlmsg_batch_reset(b);
177  }
178 
179  /* check if there is any message in the batch not sent yet. */
180  if (!mnl_nlmsg_batch_is_empty(b))
181  send_batch(nl, b, portid);
182 
183  mnl_nlmsg_batch_stop(b);
184  mnl_socket_close(nl);
185 
186  return 0;
187 }