]> granicus.if.org Git - ipset/blob - lib/session.c
b1c5f5ebdce5c75155b603b27c1c15d8b7548668
[ipset] / lib / session.c
1 /* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation.
6  */
7 #include <assert.h>                             /* assert */
8 #include <endian.h>                             /* htobe64 */
9 #include <errno.h>                              /* errno */
10 #include <setjmp.h>                             /* setjmp, longjmp */
11 #include <stdio.h>                              /* snprintf */
12 #include <stdarg.h>                             /* va_* */
13 #include <stdlib.h>                             /* free */
14 #include <string.h>                             /* str* */
15 #include <unistd.h>                             /* getpagesize */
16 #include <net/ethernet.h>                       /* ETH_ALEN */
17 #include <net/if.h>                             /* IFNAMSIZ */
18
19 #include <libipset/compat.h>                    /* be64toh() */
20 #include <libipset/debug.h>                     /* D() */
21 #include <libipset/data.h>                      /* IPSET_OPT_* */
22 #include <libipset/errcode.h>                   /* ipset_errcode */
23 #include <libipset/print.h>                     /* ipset_print_* */
24 #include <libipset/types.h>                     /* struct ipset_type */
25 #include <libipset/transport.h>                 /* transport */
26 #include <libipset/mnl.h>                       /* default backend */
27 #include <libipset/utils.h>                     /* STREQ */
28 #include <libipset/ui.h>                        /* IPSET_ENV_* */
29 #include <libipset/session.h>                   /* prototypes */
30
31 #define IPSET_NEST_MAX  4
32
33 /* The session structure */
34 struct ipset_session {
35         const struct ipset_transport *transport;/* Transport protocol */
36         struct ipset_handle *handle;            /* Transport handler */
37         struct ipset_data *data;                /* Input/output data */
38         /* Command state */
39         enum ipset_cmd cmd;                     /* Current command */
40         uint32_t lineno;                        /* Current lineno in restore mode */
41         uint32_t printed_set;                   /* Printed sets so far */
42         char saved_setname[IPSET_MAXNAMELEN];   /* Saved setname */
43         const struct ipset_type *saved_type;    /* Saved type */
44         struct nlattr *nested[IPSET_NEST_MAX];  /* Pointer to nest levels */
45         uint8_t nestid;                         /* Current nest level */
46         bool version_checked;                   /* Version checked */
47         /* Output buffer */
48         char outbuf[IPSET_OUTBUFLEN];           /* Output buffer */
49         enum ipset_output_mode mode;            /* Output mode */
50         ipset_outfn outfn;                      /* Output function */
51         /* Error/warning reporting */
52         char report[IPSET_ERRORBUFLEN];         /* Error/report buffer */
53         char *errmsg;
54         char *warnmsg;
55         uint8_t envopts;                        /* Session env opts */
56         /* Kernel message buffer */
57         size_t bufsize;
58         void *buffer;
59 };
60
61 /*
62  * Glue functions
63  */
64
65 /**
66  * ipset_session_data - return pointer to the data
67  * @session: session structure
68  *
69  * Returns the pointer to the data structure of the session.
70  */
71 struct ipset_data *
72 ipset_session_data(const struct ipset_session *session)
73 {
74         assert(session);
75         return session->data;
76 }
77
78 /**
79  * ipset_session_handle - return pointer to the handle
80  * @session: session structure
81  *
82  * Returns the pointer to the transport handle structure of the session.
83  */
84 struct ipset_handle *
85 ipset_session_handle(const struct ipset_session *session)
86 {
87         assert(session);
88         return session->handle;
89 }
90
91 /**
92  * ipset_saved_type - return pointer to the saved type
93  * @session: session structure
94  *
95  * Returns the pointer to the saved type from the last ipset_cmd
96  * It is required to decode type-specific error codes in restore mode.
97  */
98 const struct ipset_type *
99 ipset_saved_type(const struct ipset_session *session)
100 {
101         assert(session);
102         return session->saved_type;
103 }
104
105 /**
106  * ipset_session_lineno - set session lineno
107  * @session: session structure
108  *
109  * Set session lineno to report parser errors correctly.
110  */
111 void
112 ipset_session_lineno(struct ipset_session *session, uint32_t lineno)
113 {
114         assert(session);
115         session->lineno = lineno;
116 }
117
118 /*
119  * Environment options
120  */
121
122 /**
123  * ipset_envopt_parse - parse/set environment option
124  * @session: session structure
125  * @opt: environment option
126  * @arg: option argument (unused)
127  *
128  * Parse and set an environment option.
129  *
130  * Returns 0 on success or a negative error code.
131  */
132 int
133 ipset_envopt_parse(struct ipset_session *session, int opt,
134                    const char *arg UNUSED)
135 {
136         assert(session);
137
138         switch (opt) {
139         case IPSET_ENV_SORTED:
140         case IPSET_ENV_QUIET:
141         case IPSET_ENV_RESOLVE:
142         case IPSET_ENV_EXIST:
143         case IPSET_ENV_LIST_SETNAME:
144         case IPSET_ENV_LIST_HEADER:
145                 session->envopts |= opt;
146                 return 0;
147         default:
148                 break;
149         }
150         return -1;
151 }
152
153 /**
154  * ipset_envopt_test - test environment option
155  * @session: session structure
156  * @opt: environment option
157  *
158  * Test whether the environment option is set in the session.
159  *
160  * Returns true or false.
161  */
162 bool
163 ipset_envopt_test(struct ipset_session *session, enum ipset_envopt opt)
164 {
165         assert(session);
166         return session->envopts & opt;
167 }
168
169 /**
170  * ipset_session_output - set the session output mode
171  * @session: session structure
172  * @mode: output mode
173  *
174  * Set the output mode for the session.
175  *
176  * Returns 0 on success or a negative error code.
177  */
178 int
179 ipset_session_output(struct ipset_session *session,
180                      enum ipset_output_mode mode)
181 {
182         assert(session);
183         session->mode = mode;
184         return 0;
185 }
186
187 /*
188  * Error and warning reporting
189  */
190
191 /**
192  * ipset_session_report - fill the report buffer
193  * @session: session structure
194  * @type: report type
195  * @fmt: message format
196  *
197  * Fill the report buffer with an error or warning message.
198  * Depending on the report type, set the error or warning
199  * message pointer.
200  *
201  * Returns -1.
202  */
203 int __attribute__((format(printf, 3, 4)))
204 ipset_session_report(struct ipset_session *session,
205                      enum ipset_err_type type,
206                      const char *fmt, ...)
207 {
208         int len, offset = 0;
209         va_list args;
210
211         assert(session);
212         assert(fmt);
213
214         if (session->lineno != 0 && type == IPSET_ERROR) {
215                 sprintf(session->report, "Error in line %u: ",
216                         session->lineno);
217         }
218         offset = strlen(session->report);
219
220         va_start(args, fmt);
221         len = vsnprintf(session->report + offset,
222                         IPSET_ERRORBUFLEN - 1 - offset,
223                         fmt, args);
224         va_end(args);
225
226         if (len >= IPSET_ERRORBUFLEN - 1 - offset)
227                 session->report[IPSET_ERRORBUFLEN - 1] = '\0';
228         if (strlen(session->report) < IPSET_ERRORBUFLEN - 1)
229                 strcat(session->report, "\n");
230
231         if (type == IPSET_ERROR) {
232                 session->errmsg = session->report;
233                 session->warnmsg = NULL;
234                 ipset_data_reset(ipset_session_data(session));
235         } else {
236                 session->errmsg = NULL;
237                 session->warnmsg = session->report;
238         }
239         return -1;
240 }
241
242 /**
243  * ipset_session_reset - reset the report buffer
244  * @session: session structure
245  *
246  * Reset the report buffer, the error and warning pointers.
247  */
248 void
249 ipset_session_report_reset(struct ipset_session *session)
250 {
251         assert(session);
252         session->report[0] = '\0';
253         session->errmsg = session->warnmsg = NULL;
254 }
255
256 /**
257  * ipset_session_error - return the report buffer as error
258  * @session: session structure
259  *
260  * Return the pointer to the report buffer as an error report.
261  * If there is no error message in the buffer, NULL returned.
262  */
263 const char *
264 ipset_session_error(const struct ipset_session *session)
265 {
266         assert(session);
267
268         return session->errmsg;
269 }
270
271 /**
272  * ipset_session_warning - return the report buffer as warning
273  * @session: session structure
274  *
275  * Return the pointer to the report buffer as a warning report.
276  * If there is no warning message in the buffer, NULL returned.
277  */
278 const char *
279 ipset_session_warning(const struct ipset_session *session)
280 {
281         assert(session);
282
283         return session->warnmsg;
284 }
285
286 /*
287  * Receive data from the kernel
288  */
289
290 struct ipset_attr_policy {
291         uint16_t type;
292         uint16_t len;
293         enum ipset_opt opt;
294 };
295
296 /* Attribute policies and mapping to options */
297 static const struct ipset_attr_policy cmd_attrs[] = {
298         [IPSET_ATTR_PROTOCOL] = {
299                 .type = MNL_TYPE_U8,
300         },
301         [IPSET_ATTR_SETNAME] = {
302                 .type = MNL_TYPE_NUL_STRING,
303                 .opt  = IPSET_SETNAME,
304                 .len  = IPSET_MAXNAMELEN,
305         },
306         [IPSET_ATTR_TYPENAME] = {
307                 .type = MNL_TYPE_NUL_STRING,
308                 .opt = IPSET_OPT_TYPENAME,
309                 .len  = IPSET_MAXNAMELEN,
310         },
311         /* IPSET_ATTR_SETNAME2 is an alias for IPSET_ATTR_TYPENAME */
312         [IPSET_ATTR_REVISION] = {
313                 .type = MNL_TYPE_U8,
314                 .opt = IPSET_OPT_REVISION,
315         },
316         [IPSET_ATTR_FAMILY] = {
317                 .type = MNL_TYPE_U8,
318                 .opt = IPSET_OPT_FAMILY,
319         },
320         [IPSET_ATTR_FLAGS] = {
321                 .type = MNL_TYPE_U32,
322                 .opt = IPSET_OPT_FLAGS,
323         },
324         [IPSET_ATTR_DATA] = {
325                 .type = MNL_TYPE_NESTED,
326         },
327         [IPSET_ATTR_ADT] = {
328                 .type = MNL_TYPE_NESTED,
329         },
330         [IPSET_ATTR_REVISION_MIN] = {
331                 .type = MNL_TYPE_U8,
332                 .opt = IPSET_OPT_REVISION_MIN,
333         },
334         /* IPSET_ATTR_PROTOCOL_MIN is an alias for IPSET_ATTR_REVISION_MIN */
335         [IPSET_ATTR_LINENO] = {
336                 .type = MNL_TYPE_U32,
337                 .opt = IPSET_OPT_LINENO,
338         },
339 };
340
341 static const struct ipset_attr_policy create_attrs[] = {
342         [IPSET_ATTR_IP] = {
343                 .type = MNL_TYPE_NESTED,
344                 .opt = IPSET_OPT_IP,
345         },
346         [IPSET_ATTR_IP_TO] = {
347                 .type = MNL_TYPE_NESTED,
348                 .opt = IPSET_OPT_IP_TO,
349         },
350         [IPSET_ATTR_CIDR] = {
351                 .type = MNL_TYPE_U8,
352                 .opt = IPSET_OPT_CIDR,
353         },
354         [IPSET_ATTR_PORT] = {
355                 .type = MNL_TYPE_U16,
356                 .opt = IPSET_OPT_PORT,
357         },
358         [IPSET_ATTR_PORT_TO] = {
359                 .type = MNL_TYPE_U16,
360                 .opt = IPSET_OPT_PORT_TO,
361         },
362         [IPSET_ATTR_TIMEOUT] = {
363                 .type = MNL_TYPE_U32,
364                 .opt = IPSET_OPT_TIMEOUT,
365         },
366         [IPSET_ATTR_PROTO] = {
367                 .type = MNL_TYPE_U8,
368                 .opt = IPSET_OPT_PROTO,
369         },
370         [IPSET_ATTR_CADT_FLAGS] = {
371                 .type = MNL_TYPE_U32,
372                 .opt = IPSET_OPT_CADT_FLAGS,
373         },
374         [IPSET_ATTR_GC] = {
375                 .type = MNL_TYPE_U32,
376                 .opt = IPSET_OPT_GC,
377         },
378         [IPSET_ATTR_HASHSIZE] = {
379                 .type = MNL_TYPE_U32,
380                 .opt = IPSET_OPT_HASHSIZE,
381         },
382         [IPSET_ATTR_MAXELEM] = {
383                 .type = MNL_TYPE_U32,
384                 .opt = IPSET_OPT_MAXELEM,
385         },
386         [IPSET_ATTR_MARKMASK] = {
387                 .type = MNL_TYPE_U32,
388                 .opt = IPSET_OPT_MARKMASK,
389         },
390         [IPSET_ATTR_NETMASK] = {
391                 .type = MNL_TYPE_U8,
392                 .opt = IPSET_OPT_NETMASK,
393         },
394         [IPSET_ATTR_PROBES] = {
395                 .type = MNL_TYPE_U8,
396                 .opt = IPSET_OPT_PROBES,
397         },
398         [IPSET_ATTR_RESIZE] = {
399                 .type = MNL_TYPE_U8,
400                 .opt = IPSET_OPT_RESIZE,
401         },
402         [IPSET_ATTR_SIZE] = {
403                 .type = MNL_TYPE_U32,
404                 .opt = IPSET_OPT_SIZE,
405         },
406         [IPSET_ATTR_ELEMENTS] = {
407                 .type = MNL_TYPE_U32,
408                 .opt = IPSET_OPT_ELEMENTS,
409         },
410         [IPSET_ATTR_REFERENCES] = {
411                 .type = MNL_TYPE_U32,
412                 .opt = IPSET_OPT_REFERENCES,
413         },
414         [IPSET_ATTR_MEMSIZE] = {
415                 .type = MNL_TYPE_U32,
416                 .opt = IPSET_OPT_MEMSIZE,
417         },
418 };
419
420 static const struct ipset_attr_policy adt_attrs[] = {
421         [IPSET_ATTR_IP] = {
422                 .type = MNL_TYPE_NESTED,
423                 .opt = IPSET_OPT_IP,
424         },
425         [IPSET_ATTR_IP_TO] = {
426                 .type = MNL_TYPE_NESTED,
427                 .opt = IPSET_OPT_IP_TO,
428         },
429         [IPSET_ATTR_CIDR] = {
430                 .type = MNL_TYPE_U8,
431                 .opt = IPSET_OPT_CIDR,
432         },
433         [IPSET_ATTR_MARK] = {
434                 .type = MNL_TYPE_U32,
435                 .opt = IPSET_OPT_MARK,
436         },
437         [IPSET_ATTR_PORT] = {
438                 .type = MNL_TYPE_U16,
439                 .opt = IPSET_OPT_PORT,
440         },
441         [IPSET_ATTR_PORT_TO] = {
442                 .type = MNL_TYPE_U16,
443                 .opt = IPSET_OPT_PORT_TO,
444         },
445         [IPSET_ATTR_PROTO] = {
446                 .type = MNL_TYPE_U8,
447                 .opt = IPSET_OPT_PROTO,
448         },
449         [IPSET_ATTR_TIMEOUT] = {
450                 .type = MNL_TYPE_U32,
451                 .opt = IPSET_OPT_TIMEOUT,
452         },
453         [IPSET_ATTR_CADT_FLAGS] = {
454                 .type = MNL_TYPE_U32,
455                 .opt = IPSET_OPT_CADT_FLAGS,
456         },
457         [IPSET_ATTR_LINENO] = {
458                 .type = MNL_TYPE_U32,
459                 .opt = IPSET_OPT_LINENO,
460         },
461         [IPSET_ATTR_ETHER] = {
462                 .type = MNL_TYPE_BINARY,
463                 .opt = IPSET_OPT_ETHER,
464                 .len  = ETH_ALEN,
465         },
466         [IPSET_ATTR_NAME] = {
467                 .type = MNL_TYPE_NUL_STRING,
468                 .opt = IPSET_OPT_NAME,
469                 .len  = IPSET_MAXNAMELEN,
470         },
471         [IPSET_ATTR_NAMEREF] = {
472                 .type = MNL_TYPE_NUL_STRING,
473                 .opt = IPSET_OPT_NAMEREF,
474                 .len  = IPSET_MAXNAMELEN,
475         },
476         [IPSET_ATTR_IP2] = {
477                 .type = MNL_TYPE_NESTED,
478                 .opt = IPSET_OPT_IP2,
479         },
480         [IPSET_ATTR_CIDR2] = {
481                 .type = MNL_TYPE_U8,
482                 .opt = IPSET_OPT_CIDR2,
483         },
484         [IPSET_ATTR_IP2_TO] = {
485                 .type = MNL_TYPE_NESTED,
486                 .opt = IPSET_OPT_IP2_TO,
487         },
488         [IPSET_ATTR_IFACE] = {
489                 .type = MNL_TYPE_NUL_STRING,
490                 .opt = IPSET_OPT_IFACE,
491                 .len  = IFNAMSIZ,
492         },
493         [IPSET_ATTR_PACKETS] = {
494                 .type = MNL_TYPE_U64,
495                 .opt = IPSET_OPT_PACKETS,
496         },
497         [IPSET_ATTR_BYTES] = {
498                 .type = MNL_TYPE_U64,
499                 .opt = IPSET_OPT_BYTES,
500         },
501         [IPSET_ATTR_COMMENT] = {
502                 .type = MNL_TYPE_NUL_STRING,
503                 .opt = IPSET_OPT_ADT_COMMENT,
504                 .len  = IPSET_MAX_COMMENT_SIZE + 1,
505         },
506         [IPSET_ATTR_SKBMARK] = {
507                 .type = MNL_TYPE_U64,
508                 .opt = IPSET_OPT_SKBMARK,
509         },
510         [IPSET_ATTR_SKBPRIO] = {
511                 .type = MNL_TYPE_U32,
512                 .opt = IPSET_OPT_SKBPRIO,
513         },
514         [IPSET_ATTR_SKBQUEUE] = {
515                 .type = MNL_TYPE_U16,
516                 .opt = IPSET_OPT_SKBQUEUE,
517         },
518         [IPSET_ATTR_PAD] = {
519                 .type = MNL_TYPE_UNSPEC,
520                 .len = 0,
521         },
522 };
523
524 static const struct ipset_attr_policy ipaddr_attrs[] = {
525         [IPSET_ATTR_IPADDR_IPV4] = {
526                 .type = MNL_TYPE_U32,
527         },
528         [IPSET_ATTR_IPADDR_IPV6] = {
529                 .type = MNL_TYPE_BINARY,
530                 .len = sizeof(union nf_inet_addr),
531         },
532 };
533
534 #ifdef IPSET_DEBUG
535 static int debug = 1;
536 #endif
537
538 static int
539 generic_data_attr_cb(const struct nlattr *attr, void *data,
540                      int attr_max, const struct ipset_attr_policy *policy)
541 {
542         const struct nlattr **tb = data;
543         int type = mnl_attr_get_type(attr);
544
545         IF_D(debug, "attr type: %u, len %u", type, attr->nla_len);
546         if (mnl_attr_type_valid(attr, attr_max) < 0) {
547                 IF_D(debug, "attr type: %u INVALID", type);
548                 return MNL_CB_ERROR;
549         }
550         if (mnl_attr_validate(attr, policy[type].type) < 0) {
551                 IF_D(debug, "attr type: %u POLICY, attrlen %u", type,
552                      mnl_attr_get_payload_len(attr));
553                 return MNL_CB_ERROR;
554         }
555         if (policy[type].type == MNL_TYPE_NUL_STRING &&
556             mnl_attr_get_payload_len(attr) > policy[type].len)
557                 return MNL_CB_ERROR;
558         tb[type] = attr;
559         return MNL_CB_OK;
560 }
561
562 static int
563 create_attr_cb(const struct nlattr *attr, void *data)
564 {
565         return generic_data_attr_cb(attr, data,
566                                     IPSET_ATTR_CREATE_MAX, create_attrs);
567 }
568
569 static int
570 adt_attr_cb(const struct nlattr *attr, void *data)
571 {
572         return generic_data_attr_cb(attr, data,
573                                     IPSET_ATTR_ADT_MAX, adt_attrs);
574 }
575
576 static int
577 ipaddr_attr_cb(const struct nlattr *attr, void *data)
578 {
579         return generic_data_attr_cb(attr, data,
580                                     IPSET_ATTR_IPADDR_MAX, ipaddr_attrs);
581 }
582
583 #define FAILURE(format, args...) \
584         { ipset_err(session, format  , ## args); return MNL_CB_ERROR; }
585
586 static int
587 attr2data(struct ipset_session *session, struct nlattr *nla[],
588           int type, const struct ipset_attr_policy attrs[])
589 {
590         struct ipset_data *data = session->data;
591         const struct ipset_attr_policy *attr;
592         const void *d;
593         uint64_t v64;
594         uint32_t v32;
595         uint16_t v16;
596         int ret;
597
598         attr = &attrs[type];
599         d = mnl_attr_get_payload(nla[type]);
600
601         if (attr->type == MNL_TYPE_UNSPEC)
602                 return 0;
603         if (attr->type == MNL_TYPE_NESTED && attr->opt) {
604                 /* IP addresses */
605                 struct nlattr *ipattr[IPSET_ATTR_IPADDR_MAX+1] = {};
606                 uint8_t family = ipset_data_family(data);
607                 int atype;
608                 D("IP attr type %u", type);
609                 if (mnl_attr_parse_nested(nla[type],
610                                           ipaddr_attr_cb, ipattr) < 0)
611                         FAILURE("Broken kernel message, cannot validate "
612                                 "IP address attribute!");
613
614                 /* Validate by hand */
615                 switch (family) {
616                 case NFPROTO_IPV4:
617                         atype = IPSET_ATTR_IPADDR_IPV4;
618                         if (!ipattr[atype])
619                                 FAILURE("Broken kernel message: IPv4 address "
620                                         "expected but not received!");
621                         if (ipattr[atype]->nla_len < sizeof(uint32_t))
622                                 FAILURE("Broken kernel message: "
623                                         "cannot validate IPv4 "
624                                         "address attribute!");
625                         break;
626                 case NFPROTO_IPV6:
627                         atype = IPSET_ATTR_IPADDR_IPV6;
628                         if (!ipattr[atype])
629                                 FAILURE("Broken kernel message: IPv6 address "
630                                         "expected but not received!");
631                         if (ipattr[atype]->nla_len < sizeof(struct in6_addr))
632                                 FAILURE("Broken kernel message: "
633                                         "cannot validate IPv6 "
634                                         "address attribute!");
635                         break;
636                 default:
637                         FAILURE("Broken kernel message: "
638                                 "IP address attribute but "
639                                 "family is unspecified!");
640                 }
641                 d = mnl_attr_get_payload(ipattr[atype]);
642         } else if (nla[type]->nla_type & NLA_F_NET_BYTEORDER) {
643                 D("netorder attr type %u", type);
644                 switch (attr->type) {
645                 case MNL_TYPE_U64: {
646                         uint64_t tmp;
647                         /* Ensure data alignment */
648                         memcpy(&tmp, d, sizeof(tmp));
649                         v64  = be64toh(tmp);
650                         d = &v64;
651                         break;
652                 }
653                 case MNL_TYPE_U32: {
654                         v32  = ntohl(*(const uint32_t *)d);
655                         d = &v32;
656                         break;
657                 }
658                 case MNL_TYPE_U16: {
659                         v16 = ntohs(*(const uint16_t *)d);
660                         d = &v16;
661                         break;
662                 }
663                 default:
664                         break;
665                 }
666         }
667 #ifdef IPSET_DEBUG
668          else
669                 D("hostorder attr type %u", type);
670         if (type == IPSET_ATTR_TYPENAME)
671                 D("nla typename %s", (const char *) d);
672 #endif
673         ret = ipset_data_set(data, attr->opt, d);
674 #ifdef IPSET_DEBUG
675         if (type == IPSET_ATTR_TYPENAME)
676                 D("nla typename %s",
677                   (const char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
678 #endif
679         return ret;
680 }
681
682 #define ATTR2DATA(session, nla, type, attrs)            \
683         if (attr2data(session, nla, type, attrs) < 0)   \
684                 return MNL_CB_ERROR
685
686 static const char cmd2name[][9] = {
687         [IPSET_CMD_NONE]        = "NONE",
688         [IPSET_CMD_CREATE]      = "CREATE",
689         [IPSET_CMD_DESTROY]     = "DESTROY",
690         [IPSET_CMD_FLUSH]       = "FLUSH",
691         [IPSET_CMD_RENAME]      = "RENAME",
692         [IPSET_CMD_SWAP]        = "SWAP",
693         [IPSET_CMD_LIST]        = "LIST",
694         [IPSET_CMD_SAVE]        = "SAVE",
695         [IPSET_CMD_ADD]         = "ADD",
696         [IPSET_CMD_DEL]         = "DEL",
697         [IPSET_CMD_TEST]        = "TEST",
698         [IPSET_CMD_HEADER]      = "HEADER",
699         [IPSET_CMD_TYPE]        = "TYPE",
700         [IPSET_CMD_PROTOCOL]    = "PROTOCOL",
701 };
702
703 static inline int
704 call_outfn(struct ipset_session *session)
705 {
706         int ret = session->outfn("%s", session->outbuf);
707
708         session->outbuf[0] = '\0';
709
710         return ret < 0 ? ret : 0;
711 }
712
713 /* Handle printing failures */
714 static jmp_buf printf_failure;
715
716 static int
717 handle_snprintf_error(struct ipset_session *session,
718                       int len, int ret, int loop)
719 {
720         if (ret < 0 || ret >= IPSET_OUTBUFLEN - len) {
721                 /* Buffer was too small, push it out and retry */
722                 D("print buffer and try again: len: %u, ret: %d", len, ret);
723                 if (loop) {
724                         ipset_err(session,
725                                 "Internal error at printing, loop detected!");
726                         longjmp(printf_failure, 1);
727                 }
728
729                 session->outbuf[len] = '\0';
730                 if (call_outfn(session)) {
731                         ipset_err(session,
732                                 "Internal error, could not print output buffer!");
733                         longjmp(printf_failure, 1);
734                 }
735                 return 1;
736         }
737         return 0;
738 }
739
740 static int __attribute__((format(printf, 2, 3)))
741 safe_snprintf(struct ipset_session *session, const char *fmt, ...)
742 {
743         va_list args;
744         int len, ret, loop = 0;
745
746         do {
747                 len = strlen(session->outbuf);
748                 D("len: %u, retry %u", len, loop);
749                 va_start(args, fmt);
750                 ret = vsnprintf(session->outbuf + len,
751                                 IPSET_OUTBUFLEN - len,
752                                 fmt, args);
753                 va_end(args);
754                 loop = handle_snprintf_error(session, len, ret, loop);
755         } while (loop);
756
757         return ret;
758 }
759
760 static int
761 safe_dprintf(struct ipset_session *session, ipset_printfn fn,
762              enum ipset_opt opt)
763 {
764         int len, ret, loop = 0;
765
766         do {
767                 len = strlen(session->outbuf);
768                 D("len: %u, retry %u", len, loop);
769                 ret = fn(session->outbuf + len, IPSET_OUTBUFLEN - len,
770                          session->data, opt, session->envopts);
771                 loop = handle_snprintf_error(session, len, ret, loop);
772         } while (loop);
773
774         return ret;
775 }
776
777 static int
778 list_adt(struct ipset_session *session, struct nlattr *nla[])
779 {
780         const struct ipset_data *data = session->data;
781         const struct ipset_type *type;
782         const struct ipset_arg *arg;
783         int i, found = 0;
784
785         D("enter");
786         /* Check and load type, family */
787         if (!ipset_data_test(data, IPSET_OPT_TYPE))
788                 type = ipset_type_get(session, IPSET_CMD_ADD);
789         else
790                 type = ipset_data_get(data, IPSET_OPT_TYPE);
791
792         if (type == NULL)
793                 return MNL_CB_ERROR;
794
795         for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
796                 if (nla[i]) {
797                         found++;
798                         ATTR2DATA(session, nla, i, adt_attrs);
799         }
800         D("attr found %u", found);
801         if (!found)
802                 return MNL_CB_OK;
803
804         switch (session->mode) {
805         case IPSET_LIST_SAVE:
806                 safe_snprintf(session, "add %s ", ipset_data_setname(data));
807                 break;
808         case IPSET_LIST_XML:
809                 safe_snprintf(session, "<member><elem>");
810                 break;
811         case IPSET_LIST_PLAIN:
812         default:
813                 break;
814         }
815
816         safe_dprintf(session, ipset_print_elem, IPSET_OPT_ELEM);
817         if (session->mode == IPSET_LIST_XML)
818                 safe_snprintf(session, "</elem>");
819
820         for (i = 0; type->cmd[IPSET_ADD].args[i] != IPSET_ARG_NONE; i++) {
821                 arg = ipset_keyword(type->cmd[IPSET_ADD].args[i]);
822                 D("print arg opt %u (%s) %s", arg->opt, arg->name[0],
823                    ipset_data_test(data, arg->opt) ? "(yes)" : "(missing)");
824                 if (!(arg->print && ipset_data_test(data, arg->opt)))
825                         continue;
826                 switch (session->mode) {
827                 case IPSET_LIST_SAVE:
828                 case IPSET_LIST_PLAIN:
829                         if (arg->has_arg == IPSET_NO_ARG) {
830                                 safe_snprintf(session, " %s", arg->name[0]);
831                                 break;
832                         }
833                         safe_snprintf(session, " %s ", arg->name[0]);
834                         safe_dprintf(session, arg->print, arg->opt);
835                         break;
836                 case IPSET_LIST_XML:
837                         if (arg->has_arg == IPSET_NO_ARG) {
838                                 safe_snprintf(session,
839                                               "<%s/>", arg->name[0]);
840                                 break;
841                         }
842                         safe_snprintf(session, "<%s>", arg->name[0]);
843                         safe_dprintf(session, arg->print, arg->opt);
844                         safe_snprintf(session, "</%s>", arg->name[0]);
845                         break;
846                 default:
847                         break;
848                 }
849         }
850
851         if (session->mode == IPSET_LIST_XML)
852                 safe_snprintf(session, "</member>\n");
853         else
854                 safe_snprintf(session, "\n");
855
856         return MNL_CB_OK;
857 }
858
859 #define FAMILY_TO_STR(f)                \
860         ((f) == NFPROTO_IPV4 ? "inet" : \
861          (f) == NFPROTO_IPV6 ? "inet6" : "any")
862
863 static int
864 list_create(struct ipset_session *session, struct nlattr *nla[])
865 {
866         const struct ipset_data *data = session->data;
867         const struct ipset_type *type;
868         const struct ipset_arg *arg;
869         uint8_t family;
870         int i;
871
872         for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
873                 if (nla[i]) {
874                         D("add attr %u, opt %u", i, create_attrs[i].opt);
875                         ATTR2DATA(session, nla, i, create_attrs);
876                 }
877
878         type = ipset_type_check(session);
879         if (type == NULL)
880                 return MNL_CB_ERROR;
881         family = ipset_data_family(data);
882
883         switch (session->mode) {
884         case IPSET_LIST_SAVE:
885                 safe_snprintf(session, "create %s %s",
886                               ipset_data_setname(data),
887                               type->name);
888                 break;
889         case IPSET_LIST_PLAIN:
890                 safe_snprintf(session, "%sName: %s\n"
891                               "Type: %s\nRevision: %u\nHeader:",
892                               session->printed_set ? "\n" : "",
893                               ipset_data_setname(data),
894                               type->name, type->revision);
895                 break;
896         case IPSET_LIST_XML:
897                 safe_snprintf(session,
898                               "<ipset name=\"%s\">\n"
899                               "<type>%s</type>\n"
900                               "<revision>%u</revision>\n"
901                               "<header>",
902                               ipset_data_setname(data),
903                               type->name, type->revision);
904                 break;
905         default:
906                 break;
907         }
908
909         D("type %s, rev %u", type->name, type->revision);
910         for (i = 0; type->cmd[IPSET_CREATE].args[i] != IPSET_ARG_NONE; i++) {
911                 arg = ipset_keyword(type->cmd[IPSET_CREATE].args[i]);
912                 D("create print arg opt %u (%s) %s", arg->opt,
913                    arg->name[0] ? arg->name[0] : "",
914                    ipset_data_test(data, arg->opt) ? "(yes)" : "(missing)");
915                 if (!arg->print ||
916                     !ipset_data_test(data, arg->opt) ||
917                     (arg->opt == IPSET_OPT_FAMILY &&
918                      family == type->family))
919                         continue;
920                 switch (session->mode) {
921                 case IPSET_LIST_SAVE:
922                 case IPSET_LIST_PLAIN:
923                         if (arg->has_arg == IPSET_NO_ARG) {
924                                 safe_snprintf(session, " %s", arg->name[0]);
925                                 break;
926                         }
927                         safe_snprintf(session, " %s ", arg->name[0]);
928                         safe_dprintf(session, arg->print, arg->opt);
929                         break;
930                 case IPSET_LIST_XML:
931                         if (arg->has_arg == IPSET_NO_ARG) {
932                                 safe_snprintf(session,
933                                               "<%s/>", arg->name[0]);
934                                 break;
935                         }
936                         safe_snprintf(session, "<%s>", arg->name[0]);
937                         safe_dprintf(session, arg->print, arg->opt);
938                         safe_snprintf(session, "</%s>", arg->name[0]);
939                         break;
940                 default:
941                         break;
942                 }
943         }
944         switch (session->mode) {
945         case IPSET_LIST_SAVE:
946                 safe_snprintf(session, "\n");
947                 break;
948         case IPSET_LIST_PLAIN:
949                 safe_snprintf(session, "\nSize in memory: ");
950                 safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
951                 safe_snprintf(session, "\nReferences: ");
952                 safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
953                 if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
954                         safe_snprintf(session, "\nNumber of entries: ");
955                         safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
956                 }
957                 safe_snprintf(session,
958                         session->envopts & IPSET_ENV_LIST_HEADER ?
959                         "\n" : "\nMembers:\n");
960                 break;
961         case IPSET_LIST_XML:
962                 safe_snprintf(session, "\n<memsize>");
963                 safe_dprintf(session, ipset_print_number, IPSET_OPT_MEMSIZE);
964                 safe_snprintf(session, "</memsize>\n<references>");
965                 safe_dprintf(session, ipset_print_number, IPSET_OPT_REFERENCES);
966                 safe_snprintf(session, "</references>\n");
967                 if (ipset_data_test(data, IPSET_OPT_ELEMENTS)) {
968                         safe_snprintf(session, "<numentries>");
969                         safe_dprintf(session, ipset_print_number, IPSET_OPT_ELEMENTS);
970                         safe_snprintf(session, "</numentries>\n");
971                 }
972                 safe_snprintf(session,
973                         session->envopts & IPSET_ENV_LIST_HEADER ?
974                         "</header>\n" :
975                         "</header>\n<members>\n");
976                 break;
977         default:
978                 break;
979         }
980         session->printed_set++;
981
982         return MNL_CB_OK;
983 }
984
985 static int
986 print_set_done(struct ipset_session *session, bool callback_done)
987 {
988         D("called for %s", session->saved_setname[0] == '\0'
989                 ? "NONE" : session->saved_setname);
990         switch (session->mode) {
991         case IPSET_LIST_XML:
992                 if (session->envopts & IPSET_ENV_LIST_SETNAME)
993                         break;
994                 if (session->envopts & IPSET_ENV_LIST_HEADER) {
995                         if (session->saved_setname[0] != '\0')
996                                 safe_snprintf(session, "</ipset>\n");
997                         break;
998                 }
999                 if (session->saved_setname[0] != '\0')
1000                         safe_snprintf(session, "</members>\n</ipset>\n");
1001                 break;
1002         default:
1003                 break;
1004         }
1005         if (callback_done && session->mode == IPSET_LIST_XML)
1006                 safe_snprintf(session, "</ipsets>\n");
1007         return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_STOP;
1008 }
1009
1010 static int
1011 callback_list(struct ipset_session *session, struct nlattr *nla[],
1012               enum ipset_cmd cmd)
1013 {
1014         struct ipset_data *data = session->data;
1015
1016         if (setjmp(printf_failure)) {
1017                 session->saved_setname[0] = '\0';
1018                 session->printed_set = 0;
1019                 return MNL_CB_ERROR;
1020         }
1021
1022         if (!nla[IPSET_ATTR_SETNAME])
1023                 FAILURE("Broken %s kernel message: missing setname!",
1024                         cmd2name[cmd]);
1025
1026         ATTR2DATA(session, nla, IPSET_ATTR_SETNAME, cmd_attrs);
1027         D("setname %s", ipset_data_setname(data));
1028         if (session->envopts & IPSET_ENV_LIST_SETNAME &&
1029             session->mode != IPSET_LIST_SAVE) {
1030                 if (session->mode == IPSET_LIST_XML)
1031                         safe_snprintf(session, "<ipset name=\"%s\"/>\n",
1032                                       ipset_data_setname(data));
1033                 else
1034                         safe_snprintf(session, "%s\n",
1035                                       ipset_data_setname(data));
1036                 return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
1037         }
1038
1039         if (STREQ(ipset_data_setname(data), session->saved_setname)) {
1040                 /* Header part already seen */
1041                 if (ipset_data_test(data, IPSET_OPT_TYPE) &&
1042                     nla[IPSET_ATTR_DATA] != NULL)
1043                         FAILURE("Broken %s kernel message: "
1044                                 "extra DATA received!", cmd2name[cmd]);
1045         } else {
1046                 if (nla[IPSET_ATTR_DATA] == NULL)
1047                         FAILURE("Broken %s kernel message: "
1048                                 "missing DATA part!", cmd2name[cmd]);
1049
1050                 /* Close previous set printing */
1051                 if (session->saved_setname[0] != '\0')
1052                         print_set_done(session, false);
1053         }
1054
1055         if (nla[IPSET_ATTR_DATA] != NULL) {
1056                 struct nlattr *cattr[IPSET_ATTR_CREATE_MAX+1] = {};
1057
1058                 if (!(nla[IPSET_ATTR_TYPENAME] &&
1059                       nla[IPSET_ATTR_FAMILY] &&
1060                       nla[IPSET_ATTR_REVISION]))
1061                         FAILURE("Broken %s kernel message: missing %s!",
1062                                 cmd2name[cmd],
1063                                 !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1064                                 !nla[IPSET_ATTR_FAMILY] ? "family" :
1065                                 "revision");
1066
1067                 /* Reset CREATE specific flags */
1068                 ipset_data_flags_unset(data, IPSET_CREATE_FLAGS);
1069                 D("nla typename %s",
1070                   (char *) mnl_attr_get_payload(nla[IPSET_ATTR_TYPENAME]));
1071
1072                 ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
1073                 ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1074                 ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
1075                 D("head: family %u, typename %s",
1076                   ipset_data_family(data),
1077                   (const char *) ipset_data_get(data, IPSET_OPT_TYPENAME));
1078                 if (mnl_attr_parse_nested(nla[IPSET_ATTR_DATA],
1079                                           create_attr_cb, cattr) < 0)
1080                         FAILURE("Broken %s kernel message: "
1081                                 "cannot validate DATA attributes!",
1082                                 cmd2name[cmd]);
1083                 if (list_create(session, cattr) != MNL_CB_OK)
1084                         return MNL_CB_ERROR;
1085                 strcpy(session->saved_setname, ipset_data_setname(data));
1086         }
1087
1088         if (nla[IPSET_ATTR_ADT] != NULL) {
1089                 struct nlattr *tb, *adt[IPSET_ATTR_ADT_MAX+1];
1090
1091                 mnl_attr_for_each_nested(tb, nla[IPSET_ATTR_ADT]) {
1092                         D("ADT attributes for %s", ipset_data_setname(data));
1093                         memset(adt, 0, sizeof(adt));
1094                         /* Reset ADT specific flags */
1095                         ipset_data_flags_unset(data, IPSET_ADT_FLAGS);
1096                         if (mnl_attr_parse_nested(tb, adt_attr_cb, adt) < 0)
1097                                 FAILURE("Broken %s kernel message: "
1098                                         "cannot validate ADT attributes!",
1099                                         cmd2name[cmd]);
1100                         if (list_adt(session, adt) != MNL_CB_OK)
1101                                 return MNL_CB_ERROR;
1102                 }
1103         }
1104         return call_outfn(session) ? MNL_CB_ERROR : MNL_CB_OK;
1105 }
1106
1107 #ifndef IPSET_PROTOCOL_MIN
1108 #define IPSET_PROTOCOL_MIN      IPSET_PROTOCOL
1109 #endif
1110
1111 #ifndef IPSET_PROTOCOL_MAX
1112 #define IPSET_PROTOCOL_MAX      IPSET_PROTOCOL
1113 #endif
1114
1115 static int
1116 callback_version(struct ipset_session *session, struct nlattr *nla[])
1117 {
1118         uint8_t min, max;
1119
1120         min = max = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
1121
1122         if (nla[IPSET_ATTR_PROTOCOL_MIN]) {
1123                 min = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL_MIN]);
1124                 D("min: %u", min);
1125         }
1126
1127         if (min > IPSET_PROTOCOL_MAX || max < IPSET_PROTOCOL_MIN)
1128                 FAILURE("Cannot communicate with kernel: "
1129                         "Kernel support protocol versions %u-%u "
1130                         "while userspace supports protocol versions %u-%u",
1131                         min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
1132
1133         if (!(session->envopts & IPSET_ENV_QUIET) &&
1134             max != IPSET_PROTOCOL_MAX)
1135                 ipset_warn(session,
1136                            "Kernel support protocol versions %u-%u "
1137                            "while userspace supports protocol versions %u-%u",
1138                            min, max, IPSET_PROTOCOL_MIN, IPSET_PROTOCOL_MAX);
1139
1140         session->version_checked = true;
1141
1142         return MNL_CB_STOP;
1143 }
1144
1145 static int
1146 callback_header(struct ipset_session *session, struct nlattr *nla[])
1147 {
1148         const char *setname;
1149         const struct ipset_data *data = session->data;
1150
1151         if (!nla[IPSET_ATTR_SETNAME])
1152                 FAILURE("Broken HEADER kernel message: missing setname!");
1153
1154         setname = mnl_attr_get_str(nla[IPSET_ATTR_SETNAME]);
1155         if (!STREQ(setname, ipset_data_setname(data)))
1156                 FAILURE("Broken HEADER kernel message: sent setname `%s' "
1157                         "does not match with received one `%s'!",
1158                         ipset_data_setname(data), setname);
1159
1160         if (!(nla[IPSET_ATTR_TYPENAME] &&
1161               nla[IPSET_ATTR_REVISION] &&
1162               nla[IPSET_ATTR_FAMILY]))
1163                 FAILURE("Broken HEADER kernel message: "
1164                         "missing attribute '%s'!",
1165                         !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1166                         !nla[IPSET_ATTR_REVISION] ? "revision" :
1167                         "family");
1168
1169         ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1170         ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
1171         ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
1172         D("got family: %u", ipset_data_family(session->data));
1173
1174         return MNL_CB_STOP;
1175 }
1176
1177 static int
1178 callback_type(struct ipset_session *session, struct nlattr *nla[])
1179 {
1180         const struct ipset_data *data = session->data;
1181         const char *typename, *orig;
1182
1183         if (!(nla[IPSET_ATTR_TYPENAME] &&
1184               nla[IPSET_ATTR_REVISION] &&
1185               nla[IPSET_ATTR_FAMILY]))
1186                 FAILURE("Broken TYPE kernel message: "
1187                         "missing attribute '%s'!",
1188                         !nla[IPSET_ATTR_TYPENAME] ? "typename" :
1189                         !nla[IPSET_ATTR_REVISION] ? "revision" :
1190                         "family");
1191
1192         typename = mnl_attr_get_str(nla[IPSET_ATTR_TYPENAME]);
1193         orig = ipset_data_get(data, IPSET_OPT_TYPENAME);
1194         if (!STREQ(typename, orig))
1195                 FAILURE("Broken TYPE kernel message: sent typename `%s' "
1196                         "does not match with received one `%s'!",
1197                         orig, typename);
1198
1199         ATTR2DATA(session, nla, IPSET_ATTR_TYPENAME, cmd_attrs);
1200         ATTR2DATA(session, nla, IPSET_ATTR_REVISION, cmd_attrs);
1201         ATTR2DATA(session, nla, IPSET_ATTR_FAMILY, cmd_attrs);
1202         if (nla[IPSET_ATTR_REVISION_MIN])
1203                 ATTR2DATA(session, nla, IPSET_ATTR_REVISION_MIN, cmd_attrs);
1204
1205         return MNL_CB_STOP;
1206 }
1207
1208 static int
1209 cmd_attr_cb(const struct nlattr *attr, void *data)
1210 {
1211         return generic_data_attr_cb(attr, data, IPSET_ATTR_CMD_MAX, cmd_attrs);
1212 }
1213
1214 #if 0
1215 static int
1216 mnl_attr_parse_dbg(const struct nlmsghdr *nlh, int offset,
1217                    mnl_attr_cb_t cb, void *data)
1218 {
1219         int ret = MNL_CB_OK;
1220         struct nlattr *attr = mnl_nlmsg_get_payload_offset(nlh, offset);
1221         int len = nlh->nlmsg_len - MNL_NLMSG_HDRLEN - MNL_ALIGN(offset);
1222
1223         while (mnl_attr_ok(attr, len)) {
1224                 D("attr: type %u, attrlen %u, len %u",
1225                   mnl_attr_get_type(attr), attr->nla_len, len);
1226                 if (cb && (ret = cb(attr, data)) <= MNL_CB_STOP)
1227                         return ret;
1228                 attr = mnl_attr_next(attr, &len);
1229         }
1230         return ret;
1231 }
1232 #endif
1233
1234 static int
1235 callback_data(const struct nlmsghdr *nlh, void *data)
1236 {
1237         struct ipset_session *session = data;
1238         struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
1239         uint8_t proto, cmd;
1240         int ret = MNL_CB_OK, nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
1241
1242         D("called, nlmsg_len %u", nlh->nlmsg_len);
1243         cmd = ipset_get_nlmsg_type(nlh);
1244         if (cmd == IPSET_CMD_LIST && session->cmd == IPSET_CMD_SAVE)
1245                 /* Kernel always send IPSET_CMD_LIST */
1246                 cmd = IPSET_CMD_SAVE;
1247
1248         if (cmd != session->cmd)
1249                 FAILURE("Protocol error, we sent command %s "
1250                         "and received %s[%u]",
1251                         cmd2name[session->cmd],
1252                         cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
1253
1254         if (mnl_attr_parse(nlh, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
1255                 FAILURE("Broken %s kernel message: "
1256                         "cannot validate and parse attributes",
1257                         cmd2name[cmd]);
1258
1259         if (!nla[IPSET_ATTR_PROTOCOL])
1260                 FAILURE("Sad, sad day: kernel message %s "
1261                         "does not carry the protocol version.",
1262                         cmd2name[cmd]);
1263
1264         proto = mnl_attr_get_u8(nla[IPSET_ATTR_PROTOCOL]);
1265
1266         /* Check protocol */
1267         if (cmd != IPSET_CMD_PROTOCOL && proto != IPSET_PROTOCOL)
1268                 FAILURE("Giving up: kernel protocol version %u "
1269                         "does not match our protocol version %u",
1270                         proto, IPSET_PROTOCOL);
1271
1272         D("Message: %s", cmd2name[cmd]);
1273         switch (cmd) {
1274         case IPSET_CMD_LIST:
1275         case IPSET_CMD_SAVE:
1276                 ret = callback_list(session, nla, cmd);
1277                 D("flag multi: %u", nlh->nlmsg_flags & NLM_F_MULTI);
1278                 if (ret >= MNL_CB_STOP && !(nlh->nlmsg_flags & NLM_F_MULTI))
1279                         ret = print_set_done(session, false);
1280                 break;
1281         case IPSET_CMD_PROTOCOL:
1282                 if (!session->version_checked)
1283                         ret = callback_version(session, nla);
1284                 break;
1285         case IPSET_CMD_HEADER:
1286                 ret = callback_header(session, nla);
1287                 break;
1288         case IPSET_CMD_TYPE:
1289                 ret = callback_type(session, nla);
1290                 break;
1291         default:
1292                 FAILURE("Data message received when not expected at %s",
1293                         cmd2name[session->cmd]);
1294         }
1295         D("return code: %s", ret == MNL_CB_STOP ? "stop" :
1296                              ret == MNL_CB_OK ? "ok" : "error");
1297         return ret;
1298 }
1299
1300 static int
1301 callback_done(const struct nlmsghdr *nlh UNUSED, void *data)
1302 {
1303         struct ipset_session *session = data;
1304
1305         D(" called");
1306         if (session->cmd == IPSET_CMD_LIST || session->cmd == IPSET_CMD_SAVE)
1307                 return print_set_done(session, true);
1308
1309         FAILURE("Invalid message received in non LIST or SAVE state.");
1310 }
1311
1312 static int
1313 decode_errmsg(struct ipset_session *session, const struct nlmsghdr *nlh)
1314 {
1315         const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
1316         const struct nlmsghdr *msg = &err->msg;
1317         struct nlattr *nla[IPSET_ATTR_CMD_MAX+1] = {};
1318         enum ipset_cmd cmd;
1319         int nfmsglen = MNL_ALIGN(sizeof(struct nfgenmsg));
1320
1321         if (nlh->nlmsg_len < (uint32_t) MNL_ALIGN(sizeof(struct nlmsgerr)) ||
1322             nlh->nlmsg_len < MNL_ALIGN(sizeof(struct nlmsgerr))
1323                              + msg->nlmsg_len)
1324                 FAILURE("Broken error report message received.");
1325
1326         cmd = ipset_get_nlmsg_type(msg);
1327         D("nlsmg_len: %u", msg->nlmsg_len);
1328         if (cmd != session->cmd)
1329                 FAILURE("Protocol error, we sent command %s "
1330                         "and received error report for %s[%u]",
1331                         cmd2name[session->cmd],
1332                         cmd < IPSET_MSG_MAX ? cmd2name[cmd] : "unknown", cmd);
1333
1334         if (mnl_attr_parse(msg, nfmsglen, cmd_attr_cb, nla) < MNL_CB_STOP)
1335                 FAILURE("Broken %s error report message: "
1336                         "cannot validate attributes",
1337                         cmd2name[cmd]);
1338
1339         if (!nla[IPSET_ATTR_PROTOCOL])
1340                 FAILURE("Broken %s error report message: "
1341                         "missing protocol attribute",
1342                         cmd2name[cmd]);
1343
1344         if (nla[IPSET_ATTR_LINENO]) {
1345                 session->lineno = mnl_attr_get_u32(nla[IPSET_ATTR_LINENO]);
1346                 if (nla[IPSET_ATTR_LINENO]->nla_type & NLA_F_NET_BYTEORDER)
1347                         session->lineno = ntohl(session->lineno);
1348         }
1349
1350         return ipset_errcode(session, cmd, -err->error);
1351 }
1352
1353 static int
1354 callback_error(const struct nlmsghdr *nlh, void *cbdata)
1355 {
1356         struct ipset_session *session = cbdata;
1357         struct ipset_data *data = session->data;
1358         const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
1359         int ret = MNL_CB_ERROR;
1360
1361         D(" called, cmd %s", cmd2name[session->cmd]);
1362         if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr)))
1363                 FAILURE("Broken error message received.");
1364
1365         if (err->error == 0) {
1366                 /* ACK */
1367                 ret = MNL_CB_STOP;
1368
1369                 switch (session->cmd) {
1370                 case IPSET_CMD_CREATE:
1371                         /* Add successfully created set to the cache */
1372                         ipset_cache_add(ipset_data_setname(data),
1373                                         ipset_data_get(data, IPSET_OPT_TYPE),
1374                                         ipset_data_family(data));
1375                         break;
1376                 case IPSET_CMD_DESTROY:
1377                         /* Delete destroyed sets from the cache */
1378                         ipset_cache_del(ipset_data_setname(data));
1379                         /* Fall through */
1380                 case IPSET_CMD_FLUSH:
1381                         break;
1382                 case IPSET_CMD_RENAME:
1383                         ipset_cache_rename(ipset_data_setname(data),
1384                                            ipset_data_get(data,
1385                                                           IPSET_OPT_SETNAME2));
1386                         break;
1387                 case IPSET_CMD_SWAP:
1388                         ipset_cache_swap(ipset_data_setname(data),
1389                                          ipset_data_get(data,
1390                                                         IPSET_OPT_SETNAME2));
1391                         break;
1392                 case IPSET_CMD_TEST:
1393                         if (!(session->envopts & IPSET_ENV_QUIET)) {
1394                                 ipset_print_elem(session->report,
1395                                                  IPSET_ERRORBUFLEN,
1396                                                  session->data,
1397                                                  IPSET_OPT_NONE, 0);
1398                                 ipset_warn(session, " is in set %s.",
1399                                            ipset_data_setname(data));
1400                         }
1401                         /* Fall through */
1402                 case IPSET_CMD_ADD:
1403                 case IPSET_CMD_DEL:
1404                         break;
1405                 case IPSET_CMD_LIST:
1406                 case IPSET_CMD_SAVE:
1407                         /* No set in kernel */
1408                         print_set_done(session, true);
1409                         break;
1410                 default:
1411                         FAILURE("ACK message received to command %s[%u], "
1412                                 "which is not expected",
1413                                 session->cmd < IPSET_MSG_MAX
1414                                 ? cmd2name[session->cmd] : "unknown",
1415                                 session->cmd);
1416                 }
1417                 return ret;
1418         }
1419         D("nlmsgerr error: %u", -err->error);
1420
1421         /* Error messages */
1422
1423         /* Special case for IPSET_CMD_TEST */
1424         if (session->cmd == IPSET_CMD_TEST &&
1425             err->error == -IPSET_ERR_EXIST) {
1426                 if (!(session->envopts & IPSET_ENV_QUIET)) {
1427                         ipset_print_elem(session->report, IPSET_ERRORBUFLEN,
1428                                          session->data, IPSET_OPT_NONE, 0);
1429                         ipset_warn(session, " is NOT in set %s.",
1430                                    ipset_data_setname(data));
1431                 }
1432                 return ret;
1433         }
1434
1435         decode_errmsg(session, nlh);
1436
1437         return ret;
1438 }
1439
1440 static int
1441 callback_noop(const struct nlmsghdr *nlh UNUSED, void *data UNUSED)
1442 {
1443         return MNL_CB_OK;
1444 }
1445 /*
1446  * Build and send messages
1447  */
1448
1449 static inline int
1450 open_nested(struct ipset_session *session, struct nlmsghdr *nlh, int attr)
1451 {
1452         if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > session->bufsize)
1453                 return 1;
1454         session->nested[session->nestid++] = mnl_attr_nest_start(nlh, attr);
1455         return 0;
1456 }
1457
1458 static inline void
1459 close_nested(struct ipset_session *session, struct nlmsghdr *nlh)
1460 {
1461         mnl_attr_nest_end(nlh, session->nested[session->nestid-1]);
1462         session->nested[--session->nestid] = NULL;
1463 }
1464
1465 static size_t
1466 attr_len(const struct ipset_attr_policy *attr, uint8_t family, uint16_t *flags)
1467 {
1468         switch (attr->type) {
1469         case MNL_TYPE_NESTED:
1470                 if (attr->len)
1471                         return attr->len;
1472
1473                 *flags = NLA_F_NET_BYTEORDER;
1474                 return family == NFPROTO_IPV4 ? sizeof(uint32_t)
1475                                          : sizeof(struct in6_addr);
1476         case MNL_TYPE_U64:
1477                 *flags = NLA_F_NET_BYTEORDER;
1478                 return sizeof(uint64_t);
1479         case MNL_TYPE_U32:
1480                 *flags = NLA_F_NET_BYTEORDER;
1481                 return sizeof(uint32_t);
1482         case MNL_TYPE_U16:
1483                 *flags = NLA_F_NET_BYTEORDER;
1484                 return sizeof(uint16_t);
1485         case MNL_TYPE_U8:
1486                 return sizeof(uint8_t);
1487         default:
1488                 return attr->len;
1489         }
1490 }
1491
1492 #define BUFFER_FULL(bufsize, nlmsg_len, nestlen, attrlen)       \
1493 (nlmsg_len + nestlen + MNL_ATTR_HDRLEN + MNL_ALIGN(alen) + \
1494         MNL_ALIGN(sizeof(struct nlmsgerr)) > bufsize)
1495
1496 static int
1497 rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
1498              const void *d, int type, uint8_t family,
1499              const struct ipset_attr_policy attrs[])
1500 {
1501         const struct ipset_attr_policy *attr;
1502         int alen;
1503         uint16_t flags = 0;
1504
1505
1506         attr = &attrs[type];
1507         if (attr->type == MNL_TYPE_NESTED) {
1508                 /* IP addresses */
1509                 struct nlattr *nested;
1510                 int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4
1511                                               : IPSET_ATTR_IPADDR_IPV6;
1512
1513                 alen = attr_len(attr, family, &flags);
1514                 if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len,
1515                                 MNL_ATTR_HDRLEN, alen))
1516                         return 1;
1517                 nested = mnl_attr_nest_start(nlh, type);
1518                 D("family: %s", family == NFPROTO_IPV4 ? "INET" :
1519                                 family == NFPROTO_IPV6 ? "INET6" : "UNSPEC");
1520                 mnl_attr_put(nlh, atype | flags, alen, d);
1521                 mnl_attr_nest_end(nlh, nested);
1522
1523                 return 0;
1524         }
1525
1526         alen = attr_len(attr, family, &flags);
1527         if (BUFFER_FULL(session->bufsize, nlh->nlmsg_len, 0, alen))
1528                 return 1;
1529
1530         switch (attr->type) {
1531         case MNL_TYPE_U64: {
1532                 uint64_t value = htobe64(*(const uint64_t *)d);
1533
1534                 mnl_attr_put(nlh, type | flags, alen, &value);
1535                 return 0;
1536         }
1537         case MNL_TYPE_U32: {
1538                 uint32_t value = htonl(*(const uint32_t *)d);
1539
1540                 mnl_attr_put(nlh, type | flags, alen, &value);
1541                 return 0;
1542         }
1543         case MNL_TYPE_U16: {
1544                 uint16_t value = htons(*(const uint16_t *)d);
1545
1546                 mnl_attr_put(nlh, type | flags, alen, &value);
1547                 return 0;
1548         }
1549         case MNL_TYPE_NUL_STRING:
1550                 alen = strlen((const char *)d) + 1;
1551                 break;
1552         default:
1553                 break;
1554         }
1555
1556         mnl_attr_put(nlh, type | flags, alen, d);
1557
1558         return 0;
1559 }
1560
1561 static int
1562 data2attr(struct ipset_session *session, struct nlmsghdr *nlh,
1563           struct ipset_data *data, int type, uint8_t family,
1564           const struct ipset_attr_policy attrs[])
1565 {
1566         const struct ipset_attr_policy *attr = &attrs[type];
1567
1568         return rawdata2attr(session, nlh, ipset_data_get(data, attr->opt),
1569                             type, family, attrs);
1570 }
1571
1572 #define ADDATTR_PROTOCOL(nlh)                                           \
1573         mnl_attr_put_u8(nlh, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL)
1574
1575 #define ADDATTR(session, nlh, data, type, family, attrs)                \
1576         data2attr(session, nlh, data, type, family, attrs)
1577
1578 #define ADDATTR_SETNAME(session, nlh, data)                             \
1579         data2attr(session, nlh, data, IPSET_ATTR_SETNAME, NFPROTO_IPV4, \
1580                   cmd_attrs)
1581
1582 #define ADDATTR_IF(session, nlh, data, type, family, attrs)             \
1583         ipset_data_test(data, attrs[type].opt) ?                        \
1584                 data2attr(session, nlh, data, type, family, attrs) : 0
1585
1586 #define ADDATTR_RAW(session, nlh, data, type, attrs)                    \
1587         rawdata2attr(session, nlh, data, type, NFPROTO_IPV4, attrs)
1588
1589 static void
1590 addattr_create(struct ipset_session *session,
1591                struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
1592 {
1593         int i;
1594
1595         for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_CREATE_MAX; i++)
1596                 ADDATTR_IF(session, nlh, data, i, family, create_attrs);
1597 }
1598
1599 static int
1600 addattr_adt(struct ipset_session *session,
1601             struct nlmsghdr *nlh, struct ipset_data *data, uint8_t family)
1602 {
1603         int i;
1604
1605         for (i = IPSET_ATTR_UNSPEC + 1; i <= IPSET_ATTR_ADT_MAX; i++)
1606                 if (ADDATTR_IF(session, nlh, data, i, family, adt_attrs))
1607                         return 1;
1608         return 0;
1609 }
1610
1611 #define PRIVATE_MSG_BUFLEN      256
1612
1613 static int
1614 build_send_private_msg(struct ipset_session *session, enum ipset_cmd cmd)
1615 {
1616         char buffer[PRIVATE_MSG_BUFLEN] __attribute__ ((aligned)) = {};
1617         struct nlmsghdr *nlh = (void *)buffer;
1618         struct ipset_data *data = session->data;
1619         int len = PRIVATE_MSG_BUFLEN, ret;
1620         enum ipset_cmd saved = session->cmd;
1621
1622         /* Initialize header */
1623         session->transport->fill_hdr(session->handle, cmd, buffer, len, 0);
1624
1625         ADDATTR_PROTOCOL(nlh);
1626
1627         switch (cmd) {
1628         case IPSET_CMD_PROTOCOL:
1629                 break;
1630         case IPSET_CMD_HEADER:
1631                 if (!ipset_data_test(data, IPSET_SETNAME))
1632                         return ipset_err(session,
1633                                 "Invalid internal HEADER command: "
1634                                 "missing setname");
1635                 ADDATTR_SETNAME(session, nlh, data);
1636                 break;
1637         case IPSET_CMD_TYPE:
1638                 if (!ipset_data_test(data, IPSET_OPT_TYPENAME))
1639                         return ipset_err(session,
1640                                 "Invalid internal TYPE command: "
1641                                 "missing settype");
1642                 ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
1643                         NFPROTO_IPV4, cmd_attrs);
1644                 if (ipset_data_test(data, IPSET_OPT_FAMILY))
1645                         ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
1646                                 NFPROTO_IPV4, cmd_attrs);
1647                 else
1648                         /* bitmap:port and list:set types */
1649                         mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, NFPROTO_UNSPEC);
1650                 break;
1651         default:
1652                 return ipset_err(session, "Internal error: "
1653                                  "unknown private command %u", cmd);
1654         }
1655
1656         /* Backup, then restore real command */
1657         session->cmd = cmd;
1658         ret = session->transport->query(session->handle, buffer, len);
1659         session->cmd = saved;
1660
1661         return ret;
1662 }
1663
1664 static inline bool
1665 may_aggregate_ad(struct ipset_session *session, enum ipset_cmd cmd)
1666 {
1667         return session->lineno != 0 &&
1668                (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL) &&
1669                cmd == session->cmd &&
1670                STREQ(ipset_data_setname(session->data), session->saved_setname);
1671 }
1672
1673 static int
1674 build_msg(struct ipset_session *session, bool aggregate)
1675 {
1676         struct nlmsghdr *nlh = session->buffer;
1677         struct ipset_data *data = session->data;
1678
1679         /* Public commands */
1680         D("cmd %s, nlmsg_len: %u", cmd2name[session->cmd], nlh->nlmsg_len);
1681         if (nlh->nlmsg_len == 0) {
1682                 /* Initialize header */
1683                 aggregate = false;
1684                 session->transport->fill_hdr(session->handle,
1685                                              session->cmd,
1686                                              session->buffer,
1687                                              session->bufsize,
1688                                              session->envopts);
1689                 ADDATTR_PROTOCOL(nlh);
1690         }
1691         D("Protocol added, aggregate %s", aggregate ? "yes" : "no");
1692         switch (session->cmd) {
1693         case IPSET_CMD_CREATE: {
1694                 const struct ipset_type *type;
1695
1696                 /* Sanity checkings */
1697                 if (!ipset_data_test(data, IPSET_SETNAME))
1698                         return ipset_err(session,
1699                                 "Invalid create command: missing setname");
1700                 if (!ipset_data_test(data, IPSET_OPT_TYPE))
1701                         return ipset_err(session,
1702                                 "Invalid create command: missing settype");
1703
1704                 type = ipset_data_get(data, IPSET_OPT_TYPE);
1705                 /* Core attributes:
1706                  * setname, typename, revision, family, flags (optional) */
1707                 ADDATTR_SETNAME(session, nlh, data);
1708                 ADDATTR(session, nlh, data, IPSET_ATTR_TYPENAME,
1709                         NFPROTO_IPV4, cmd_attrs);
1710                 ADDATTR_RAW(session, nlh, &type->revision,
1711                             IPSET_ATTR_REVISION, cmd_attrs);
1712                 D("family: %u, type family %u",
1713                   ipset_data_family(data), type->family);
1714                 if (ipset_data_test(data, IPSET_OPT_FAMILY))
1715                         ADDATTR(session, nlh, data, IPSET_ATTR_FAMILY,
1716                                 NFPROTO_IPV4, cmd_attrs);
1717                 else
1718                         /* bitmap:port and list:set types */
1719                         mnl_attr_put_u8(nlh, IPSET_ATTR_FAMILY, NFPROTO_UNSPEC);
1720
1721                 /* Type-specific create attributes */
1722                 D("call open_nested");
1723                 open_nested(session, nlh, IPSET_ATTR_DATA);
1724                 addattr_create(session, nlh, data, type->family);
1725                 D("call close_nested");
1726                 close_nested(session, nlh);
1727                 break;
1728         }
1729         case IPSET_CMD_DESTROY:
1730         case IPSET_CMD_FLUSH:
1731         case IPSET_CMD_SAVE:
1732                 if (ipset_data_test(data, IPSET_SETNAME))
1733                         ADDATTR_SETNAME(session, nlh, data);
1734                 break;
1735         case IPSET_CMD_LIST: {
1736                 uint32_t flags = 0;
1737
1738                 if (session->envopts & IPSET_ENV_LIST_SETNAME)
1739                         flags |= IPSET_FLAG_LIST_SETNAME;
1740                 if (session->envopts & IPSET_ENV_LIST_HEADER)
1741                         flags |= IPSET_FLAG_LIST_HEADER;
1742                 if (ipset_data_test(data, IPSET_SETNAME))
1743                         ADDATTR_SETNAME(session, nlh, data);
1744                 if (flags && session->mode != IPSET_LIST_SAVE) {
1745                         ipset_data_set(data, IPSET_OPT_FLAGS, &flags);
1746                         ADDATTR(session, nlh, data, IPSET_ATTR_FLAGS,
1747                                 NFPROTO_IPV4, cmd_attrs);
1748                 }
1749                 break;
1750         }
1751         case IPSET_CMD_RENAME:
1752         case IPSET_CMD_SWAP:
1753                 if (!ipset_data_test(data, IPSET_SETNAME))
1754                         return ipset_err(session,
1755                                 "Invalid %s command: missing from-setname",
1756                                 session->cmd == IPSET_CMD_SWAP ? "swap" :
1757                                 "rename");
1758                 if (!ipset_data_test(data, IPSET_OPT_SETNAME2))
1759                         return ipset_err(session,
1760                                 "Invalid %s command: missing to-setname",
1761                                 session->cmd == IPSET_CMD_SWAP ? "swap" :
1762                                 "rename");
1763                 ADDATTR_SETNAME(session, nlh, data);
1764                 ADDATTR_RAW(session, nlh,
1765                             ipset_data_get(data, IPSET_OPT_SETNAME2),
1766                             IPSET_ATTR_SETNAME2, cmd_attrs);
1767                 break;
1768         case IPSET_CMD_ADD:
1769         case IPSET_CMD_DEL: {
1770                 const struct ipset_type *type;
1771
1772                 if (!aggregate) {
1773                         /* Setname, type not checked/added yet */
1774                         if (!ipset_data_test(data, IPSET_SETNAME))
1775                                 return ipset_err(session,
1776                                         "Invalid %s command: missing setname",
1777                                         session->cmd == IPSET_CMD_ADD ? "add" :
1778                                         "del");
1779
1780                         if (!ipset_data_test(data, IPSET_OPT_TYPE))
1781                                 return ipset_err(session,
1782                                         "Invalid %s command: missing settype",
1783                                         session->cmd == IPSET_CMD_ADD ? "add" :
1784                                         "del");
1785
1786                         /* Core options: setname */
1787                         ADDATTR_SETNAME(session, nlh, data);
1788                         if (session->lineno != 0) {
1789                                 /* Restore mode */
1790                                 ADDATTR_RAW(session, nlh, &session->lineno,
1791                                             IPSET_ATTR_LINENO, cmd_attrs);
1792                                 open_nested(session, nlh, IPSET_ATTR_ADT);
1793                         }
1794                 }
1795                 type = ipset_data_get(data, IPSET_OPT_TYPE);
1796                 D("family: %u, type family %u",
1797                   ipset_data_family(data), type->family);
1798                 if (open_nested(session, nlh, IPSET_ATTR_DATA)) {
1799                         D("open_nested failed");
1800                         return 1;
1801                 }
1802                 if (addattr_adt(session, nlh, data, ipset_data_family(data)) ||
1803                     ADDATTR_RAW(session, nlh, &session->lineno,
1804                                 IPSET_ATTR_LINENO, cmd_attrs)) {
1805                         /* Cancel last, unfinished nested attribute */
1806                         mnl_attr_nest_cancel(nlh,
1807                                         session->nested[session->nestid-1]);
1808                         session->nested[--session->nestid] = NULL;
1809                         return 1;
1810                 }
1811                 close_nested(session, nlh);
1812                 break;
1813         }
1814         case IPSET_CMD_TEST: {
1815                 const struct ipset_type *type;
1816                 /* Return codes are not aggregated, so tests cannot be either */
1817
1818                 /* Setname, type not checked/added yet */
1819
1820                 if (!ipset_data_test(data, IPSET_SETNAME))
1821                         return ipset_err(session,
1822                                 "Invalid test command: missing setname");
1823
1824                 if (!ipset_data_test(data, IPSET_OPT_TYPE))
1825                         return ipset_err(session,
1826                                 "Invalid test command: missing settype");
1827
1828                 type = ipset_data_get(data, IPSET_OPT_TYPE);
1829                 D("family: %u, type family %u",
1830                   ipset_data_family(data), type->family);
1831                 ADDATTR_SETNAME(session, nlh, data);
1832                 open_nested(session, nlh, IPSET_ATTR_DATA);
1833                 addattr_adt(session, nlh, data, ipset_data_family(data));
1834                 close_nested(session, nlh);
1835                 break;
1836         }
1837         default:
1838                 return ipset_err(session, "Internal error: unknown command %u",
1839                                  session->cmd);
1840         }
1841         return 0;
1842 }
1843
1844 /**
1845  * ipset_commit - commit buffered commands
1846  * @session: session structure
1847  *
1848  * Commit buffered commands, if there are any.
1849  *
1850  * Returns 0 on success or a negative error code.
1851  */
1852 int
1853 ipset_commit(struct ipset_session *session)
1854 {
1855         struct nlmsghdr *nlh;
1856         int ret = 0, i;
1857
1858         assert(session);
1859
1860         nlh = session->buffer;
1861         D("send buffer: len %u, cmd %s",
1862           nlh->nlmsg_len, cmd2name[session->cmd]);
1863         if (nlh->nlmsg_len == 0)
1864                 /* Nothing to do */
1865                 return 0;
1866
1867         /* Close nested data blocks */
1868         for (i = session->nestid - 1; i >= 0; i--)
1869                 close_nested(session, nlh);
1870
1871         /* Send buffer */
1872         ret = session->transport->query(session->handle,
1873                                         session->buffer,
1874                                         session->bufsize);
1875
1876         /* Reset saved data and nested state */
1877         session->saved_setname[0] = '\0';
1878         session->printed_set = 0;
1879         for (i = session->nestid - 1; i >= 0; i--)
1880                 session->nested[i] = NULL;
1881         session->nestid = 0;
1882         nlh->nlmsg_len = 0;
1883
1884         D("ret: %d", ret);
1885
1886         if (ret < 0) {
1887                 if (session->report[0] != '\0')
1888                         return -1;
1889                 else
1890                         return ipset_err(session,
1891                                          "Internal protocol error");
1892         }
1893         return 0;
1894 }
1895
1896 static mnl_cb_t cb_ctl[] = {
1897         [NLMSG_NOOP] = callback_noop,
1898         [NLMSG_ERROR] = callback_error,
1899         [NLMSG_DONE]  = callback_done,
1900         [NLMSG_OVERRUN] = callback_noop,
1901         [NLMSG_MIN_TYPE] = callback_data,
1902 };
1903
1904 static inline struct ipset_handle *
1905 init_transport(struct ipset_session *session)
1906 {
1907         session->handle = session->transport->init(cb_ctl, session);
1908
1909         return session->handle;
1910 }
1911
1912 /**
1913  * ipset_cmd - execute a command
1914  * @session: session structure
1915  * @cmd: command to execute
1916  * @lineno: command line number in restore mode
1917  *
1918  * Execute - or prepare/buffer in restore mode - a command.
1919  * It is the caller responsibility that the data field be filled out
1920  * with all required parameters for a successful execution.
1921  * The data field is cleared after this function call for the public
1922  * commands.
1923  *
1924  * Returns 0 on success or a negative error code.
1925  */
1926 int
1927 ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd, uint32_t lineno)
1928 {
1929         struct ipset_data *data;
1930         bool aggregate = false;
1931         int ret = -1;
1932
1933         assert(session);
1934
1935         if (cmd <= IPSET_CMD_NONE || cmd >= IPSET_MSG_MAX)
1936                 return 0;
1937
1938         /* Initialize transport method if not done yet */
1939         if (session->handle == NULL && init_transport(session) == NULL)
1940                 return ipset_err(session,
1941                                  "Cannot open session to kernel.");
1942
1943         data = session->data;
1944
1945         /* Check protocol version once */
1946         if (!session->version_checked) {
1947                 if (build_send_private_msg(session, IPSET_CMD_PROTOCOL) < 0)
1948                         return -1;
1949         }
1950
1951         /* Private commands */
1952         if (cmd == IPSET_CMD_TYPE || cmd == IPSET_CMD_HEADER)
1953                 return build_send_private_msg(session, cmd);
1954
1955         /* Check aggregatable commands */
1956         aggregate = may_aggregate_ad(session, cmd);
1957         if (!aggregate) {
1958                 /* Flush possible aggregated commands */
1959                 ret = ipset_commit(session);
1960                 if (ret < 0)
1961                         return ret;
1962         }
1963
1964         /* Real command: update lineno too */
1965         session->cmd = cmd;
1966         session->lineno = lineno;
1967
1968         /* Set default output mode */
1969         if (cmd == IPSET_CMD_LIST) {
1970                 if (session->mode == IPSET_LIST_NONE)
1971                         session->mode = IPSET_LIST_PLAIN;
1972         } else if (cmd == IPSET_CMD_SAVE) {
1973                 if (session->mode == IPSET_LIST_NONE)
1974                         session->mode = IPSET_LIST_SAVE;
1975         }
1976         /* Start the root element in XML mode */
1977         if ((cmd == IPSET_CMD_LIST || cmd == IPSET_CMD_SAVE) &&
1978             session->mode == IPSET_LIST_XML)
1979                 safe_snprintf(session, "<ipsets>\n");
1980
1981         D("next: build_msg");
1982         /* Build new message or append buffered commands */
1983         ret = build_msg(session, aggregate);
1984         D("build_msg returned %u", ret);
1985         if (ret > 0) {
1986                 /* Buffer is full, send buffered commands */
1987                 ret = ipset_commit(session);
1988                 if (ret < 0)
1989                         goto cleanup;
1990                 ret = build_msg(session, false);
1991                 D("build_msg 2 returned %u", ret);
1992         }
1993         if (ret < 0)
1994                 goto cleanup;
1995         D("past: build_msg");
1996
1997         /* We have to save the type for error handling */
1998         session->saved_type = ipset_data_get(data, IPSET_OPT_TYPE);
1999         if (session->lineno != 0 &&
2000             (cmd == IPSET_CMD_ADD || cmd == IPSET_CMD_DEL)) {
2001                 /* Save setname for the next possible aggregated restore line */
2002                 strcpy(session->saved_setname, ipset_data_setname(data));
2003                 ipset_data_reset(data);
2004                 /* Don't commit: we may aggregate next command */
2005                 ret = 0;
2006                 goto cleanup;
2007         }
2008
2009         D("call commit");
2010         ret = ipset_commit(session);
2011
2012 cleanup:
2013         D("reset data");
2014         ipset_data_reset(data);
2015         return ret;
2016 }
2017
2018 /**
2019  * ipset_session_outfn - set session output printing function
2020  *
2021  * Set the session printing function.
2022  *
2023  */
2024 int
2025 ipset_session_outfn(struct ipset_session *session, ipset_outfn outfn)
2026 {
2027         session->outfn = outfn ? outfn : printf;
2028         return 0;
2029 }
2030
2031 /**
2032  * ipset_session_init - initialize an ipset session
2033  *
2034  * Initialize an ipset session by allocating a session structure
2035  * and filling out with the initialization data.
2036  *
2037  * Returns the created session sctructure on success or NULL.
2038  */
2039 struct ipset_session *
2040 ipset_session_init(ipset_outfn outfn)
2041 {
2042         struct ipset_session *session;
2043         size_t bufsize = getpagesize();
2044
2045         /* Create session object */
2046         session = calloc(1, sizeof(struct ipset_session) + bufsize);
2047         if (session == NULL)
2048                 return NULL;
2049         session->bufsize = bufsize;
2050         session->buffer = session + 1;
2051
2052         /* The single transport method yet */
2053         session->transport = &ipset_mnl_transport;
2054
2055         /* Output function */
2056         session->outfn = outfn;
2057
2058         /* Initialize data structures */
2059         session->data = ipset_data_init();
2060         if (session->data == NULL)
2061                 goto free_session;
2062
2063         ipset_cache_init();
2064         return session;
2065
2066 free_session:
2067         free(session);
2068         return NULL;
2069 }
2070
2071 /**
2072  * ipset_session_fini - destroy an ipset session
2073  * @session: session structure
2074  *
2075  * Destroy an ipset session: release the created structures.
2076  *
2077  * Returns 0 on success or a negative error code.
2078  */
2079 int
2080 ipset_session_fini(struct ipset_session *session)
2081 {
2082         assert(session);
2083
2084         if (session->handle)
2085                 session->transport->fini(session->handle);
2086         if (session->data)
2087                 ipset_data_fini(session->data);
2088
2089         ipset_cache_fini();
2090         free(session);
2091         return 0;
2092 }
2093
2094 #ifdef IPSET_DEBUG
2095 #include "debug.c"
2096 #endif