Imported Upstream version 5.5.0
[platform/upstream/iproute2.git] / tc / m_csum.c
1 /*
2  * m_csum.c     checksum updating action
3  *
4  *              This program is free software; you can distribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors: Gregoire Baron <baronchon@n7mm.org>
10  */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <unistd.h>
16
17 #include <linux/tc_act/tc_csum.h>
18
19 #include "utils.h"
20 #include "tc_util.h"
21
22 static void
23 explain(void)
24 {
25         fprintf(stderr, "Usage: ... csum <UPDATE>\n"
26                         "Where: UPDATE := <TARGET> [<UPDATE>]\n"
27                         "       TARGET := { ip4h | icmp | igmp | tcp | udp | udplite | sctp | <SWEETS> }\n"
28                         "       SWEETS := { and | or | \'+\' }\n");
29 }
30
31 static void
32 usage(void)
33 {
34         explain();
35         exit(-1);
36 }
37
38 static int
39 parse_csum_args(int *argc_p, char ***argv_p, struct tc_csum *sel)
40 {
41         int argc = *argc_p;
42         char **argv = *argv_p;
43
44         if (argc <= 0)
45                 return -1;
46
47         while (argc > 0) {
48                 if ((matches(*argv, "iph") == 0) ||
49                     (matches(*argv, "ip4h") == 0) ||
50                     (matches(*argv, "ipv4h") == 0))
51                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_IPV4HDR;
52
53                 else if (matches(*argv, "icmp") == 0)
54                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_ICMP;
55
56                 else if (matches(*argv, "igmp") == 0)
57                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_IGMP;
58
59                 else if (matches(*argv, "tcp") == 0)
60                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_TCP;
61
62                 else if (matches(*argv, "udp") == 0)
63                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
64
65                 else if (matches(*argv, "udplite") == 0)
66                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_UDPLITE;
67
68                 else if (matches(*argv, "sctp") == 0)
69                         sel->update_flags |= TCA_CSUM_UPDATE_FLAG_SCTP;
70
71                 else if ((matches(*argv, "and") == 0) ||
72                          (matches(*argv, "or") == 0) ||
73                          (matches(*argv, "+") == 0))
74                         ; /* just ignore: ... csum iph and tcp or udp */
75                 else
76                         break;
77                 argc--;
78                 argv++;
79         }
80
81         *argc_p = argc;
82         *argv_p = argv;
83
84         return 0;
85 }
86
87 static int
88 parse_csum(struct action_util *a, int *argc_p,
89            char ***argv_p, int tca_id, struct nlmsghdr *n)
90 {
91         struct tc_csum sel = {};
92
93         int argc = *argc_p;
94         char **argv = *argv_p;
95         int ok = 0;
96         struct rtattr *tail;
97
98         while (argc > 0) {
99                 if (matches(*argv, "csum") == 0) {
100                         NEXT_ARG();
101                         if (parse_csum_args(&argc, &argv, &sel)) {
102                                 fprintf(stderr, "Illegal csum construct (%s)\n",
103                                         *argv);
104                                 explain();
105                                 return -1;
106                         }
107                         ok++;
108                         continue;
109                 } else if (matches(*argv, "help") == 0) {
110                         usage();
111                 } else {
112                         break;
113                 }
114         }
115
116         if (!ok) {
117                 explain();
118                 return -1;
119         }
120
121         if (sel.update_flags == 0) {
122                 fprintf(stderr, "Illegal csum construct, empty <UPDATE> list\n");
123                 return -1;
124         }
125
126         parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_OK);
127
128         if (argc) {
129                 if (matches(*argv, "index") == 0) {
130                         NEXT_ARG();
131                         if (get_u32(&sel.index, *argv, 10)) {
132                                 fprintf(stderr, "Illegal \"index\" (%s) <csum>\n",
133                                         *argv);
134                                 return -1;
135                         }
136                         argc--;
137                         argv++;
138                 }
139         }
140
141         tail = addattr_nest(n, MAX_MSG, tca_id);
142         addattr_l(n, MAX_MSG, TCA_CSUM_PARMS, &sel, sizeof(sel));
143         addattr_nest_end(n, tail);
144
145         *argc_p = argc;
146         *argv_p = argv;
147
148         return 0;
149 }
150
151 static int
152 print_csum(struct action_util *au, FILE *f, struct rtattr *arg)
153 {
154         struct tc_csum *sel;
155
156         struct rtattr *tb[TCA_CSUM_MAX + 1];
157
158         char *uflag_1 = "";
159         char *uflag_2 = "";
160         char *uflag_3 = "";
161         char *uflag_4 = "";
162         char *uflag_5 = "";
163         char *uflag_6 = "";
164         char *uflag_7 = "";
165         SPRINT_BUF(buf);
166
167         int uflag_count = 0;
168
169         if (arg == NULL)
170                 return -1;
171
172         parse_rtattr_nested(tb, TCA_CSUM_MAX, arg);
173
174         if (tb[TCA_CSUM_PARMS] == NULL) {
175                 fprintf(stderr, "Missing csum parameters\n");
176                 return -1;
177         }
178         sel = RTA_DATA(tb[TCA_CSUM_PARMS]);
179
180         if (sel->update_flags & TCA_CSUM_UPDATE_FLAG_IPV4HDR) {
181                 uflag_1 = "iph";
182                 uflag_count++;
183         }
184         #define CSUM_UFLAG_BUFFER(flag_buffer, flag_value, flag_string) \
185                 do {                                                    \
186                         if (sel->update_flags & flag_value) {           \
187                                 flag_buffer = uflag_count > 0 ?         \
188                                         ", " flag_string : flag_string; \
189                                 uflag_count++;                          \
190                         }                                               \
191                 } while (0)
192         CSUM_UFLAG_BUFFER(uflag_2, TCA_CSUM_UPDATE_FLAG_ICMP, "icmp");
193         CSUM_UFLAG_BUFFER(uflag_3, TCA_CSUM_UPDATE_FLAG_IGMP, "igmp");
194         CSUM_UFLAG_BUFFER(uflag_4, TCA_CSUM_UPDATE_FLAG_TCP, "tcp");
195         CSUM_UFLAG_BUFFER(uflag_5, TCA_CSUM_UPDATE_FLAG_UDP, "udp");
196         CSUM_UFLAG_BUFFER(uflag_6, TCA_CSUM_UPDATE_FLAG_UDPLITE, "udplite");
197         CSUM_UFLAG_BUFFER(uflag_7, TCA_CSUM_UPDATE_FLAG_SCTP, "sctp");
198         if (!uflag_count) {
199                 uflag_1 = "?empty";
200         }
201
202         print_string(PRINT_ANY, "kind", "%s ", "csum");
203         snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
204                  uflag_1, uflag_2, uflag_3,
205                  uflag_4, uflag_5, uflag_6, uflag_7);
206         print_string(PRINT_ANY, "csum", "(%s) ", buf);
207
208         print_action_control(f, "action ", sel->action, "\n");
209         print_uint(PRINT_ANY, "index", "\tindex %u", sel->index);
210         print_int(PRINT_ANY, "ref", " ref %d", sel->refcnt);
211         print_int(PRINT_ANY, "bind", " bind %d", sel->bindcnt);
212
213         if (show_stats) {
214                 if (tb[TCA_CSUM_TM]) {
215                         struct tcf_t *tm = RTA_DATA(tb[TCA_CSUM_TM]);
216
217                         print_tm(f, tm);
218                 }
219         }
220         print_string(PRINT_FP, NULL, "%s", "\n");
221
222         return 0;
223 }
224
225 struct action_util csum_action_util = {
226         .id = "csum",
227         .parse_aopt = parse_csum,
228         .print_aopt = print_csum,
229 };