]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
c4617b3c | 2 | * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "ofp-print.h" | |
064af421 BP |
19 | |
20 | #include <errno.h> | |
21 | #include <inttypes.h> | |
7f3adc00 | 22 | #include <sys/types.h> |
064af421 BP |
23 | #include <netinet/in.h> |
24 | #include <sys/wait.h> | |
25 | #include <stdarg.h> | |
26 | #include <stdlib.h> | |
27 | #include <ctype.h> | |
28 | ||
daff3353 | 29 | #include "bundle.h" |
10a24935 | 30 | #include "byte-order.h" |
064af421 BP |
31 | #include "compiler.h" |
32 | #include "dynamic-string.h" | |
33 | #include "flow.h" | |
53ddd40a | 34 | #include "multipath.h" |
7fa91113 | 35 | #include "nx-match.h" |
69b6be19 | 36 | #include "ofp-util.h" |
064af421 BP |
37 | #include "ofpbuf.h" |
38 | #include "openflow/openflow.h" | |
39 | #include "openflow/nicira-ext.h" | |
40 | #include "packets.h" | |
41 | #include "pcap.h" | |
e41a9130 | 42 | #include "type-props.h" |
c4617b3c | 43 | #include "unaligned.h" |
064af421 BP |
44 | #include "util.h" |
45 | ||
46 | static void ofp_print_port_name(struct ds *string, uint16_t port); | |
d2805da2 | 47 | static void ofp_print_queue_name(struct ds *string, uint32_t port); |
7fa91113 BP |
48 | static void ofp_print_error(struct ds *, int error); |
49 | ||
064af421 BP |
50 | |
51 | /* Returns a string that represents the contents of the Ethernet frame in the | |
52 | * 'len' bytes starting at 'data' to 'stream' as output by tcpdump. | |
53 | * 'total_len' specifies the full length of the Ethernet frame (of which 'len' | |
54 | * bytes were captured). | |
55 | * | |
56 | * The caller must free the returned string. | |
57 | * | |
58 | * This starts and kills a tcpdump subprocess so it's quite expensive. */ | |
59 | char * | |
67a4917b | 60 | ofp_packet_to_string(const void *data, size_t len, size_t total_len OVS_UNUSED) |
064af421 BP |
61 | { |
62 | struct ds ds = DS_EMPTY_INITIALIZER; | |
63 | struct ofpbuf buf; | |
64 | ||
65 | char command[128]; | |
66 | FILE *pcap; | |
67 | FILE *tcpdump; | |
68 | int status; | |
69 | int c; | |
70 | ||
0bc9407d | 71 | ofpbuf_use_const(&buf, data, len); |
064af421 BP |
72 | |
73 | pcap = tmpfile(); | |
74 | if (!pcap) { | |
75 | ovs_error(errno, "tmpfile"); | |
76 | return xstrdup("<error>"); | |
77 | } | |
78 | pcap_write_header(pcap); | |
79 | pcap_write(pcap, &buf); | |
80 | fflush(pcap); | |
81 | if (ferror(pcap)) { | |
82 | ovs_error(errno, "error writing temporary file"); | |
83 | } | |
84 | rewind(pcap); | |
85 | ||
6d38ac7c | 86 | snprintf(command, sizeof command, "/usr/sbin/tcpdump -t -e -n -r /dev/fd/%d 2>/dev/null", |
064af421 BP |
87 | fileno(pcap)); |
88 | tcpdump = popen(command, "r"); | |
89 | fclose(pcap); | |
90 | if (!tcpdump) { | |
91 | ovs_error(errno, "exec(\"%s\")", command); | |
92 | return xstrdup("<error>"); | |
93 | } | |
94 | ||
95 | while ((c = getc(tcpdump)) != EOF) { | |
96 | ds_put_char(&ds, c); | |
97 | } | |
98 | ||
99 | status = pclose(tcpdump); | |
100 | if (WIFEXITED(status)) { | |
101 | if (WEXITSTATUS(status)) | |
102 | ovs_error(0, "tcpdump exited with status %d", WEXITSTATUS(status)); | |
103 | } else if (WIFSIGNALED(status)) { | |
d295e8e9 | 104 | ovs_error(0, "tcpdump exited with signal %d", WTERMSIG(status)); |
064af421 BP |
105 | } |
106 | return ds_cstr(&ds); | |
107 | } | |
108 | ||
064af421 | 109 | static void |
d1e2cf21 BP |
110 | ofp_print_packet_in(struct ds *string, const struct ofp_packet_in *op, |
111 | int verbosity) | |
064af421 | 112 | { |
d1e2cf21 | 113 | size_t len = ntohs(op->header.length); |
064af421 BP |
114 | size_t data_len; |
115 | ||
116 | ds_put_format(string, " total_len=%"PRIu16" in_port=", | |
117 | ntohs(op->total_len)); | |
118 | ofp_print_port_name(string, ntohs(op->in_port)); | |
119 | ||
120 | if (op->reason == OFPR_ACTION) | |
121 | ds_put_cstr(string, " (via action)"); | |
122 | else if (op->reason != OFPR_NO_MATCH) | |
123 | ds_put_format(string, " (***reason %"PRIu8"***)", op->reason); | |
124 | ||
125 | data_len = len - offsetof(struct ofp_packet_in, data); | |
126 | ds_put_format(string, " data_len=%zu", data_len); | |
d84d4b88 | 127 | if (op->buffer_id == htonl(UINT32_MAX)) { |
064af421 BP |
128 | ds_put_format(string, " (unbuffered)"); |
129 | if (ntohs(op->total_len) != data_len) | |
130 | ds_put_format(string, " (***total_len != data_len***)"); | |
131 | } else { | |
132 | ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(op->buffer_id)); | |
133 | if (ntohs(op->total_len) < data_len) | |
134 | ds_put_format(string, " (***total_len < data_len***)"); | |
135 | } | |
136 | ds_put_char(string, '\n'); | |
137 | ||
138 | if (verbosity > 0) { | |
ae412e7d | 139 | struct flow flow; |
064af421 | 140 | struct ofpbuf packet; |
8321fb9c | 141 | |
0bc9407d | 142 | ofpbuf_use_const(&packet, op->data, data_len); |
659586ef | 143 | flow_extract(&packet, 0, ntohs(op->in_port), &flow); |
8321fb9c | 144 | flow_format(string, &flow); |
064af421 BP |
145 | ds_put_char(string, '\n'); |
146 | } | |
147 | if (verbosity > 1) { | |
148 | char *packet = ofp_packet_to_string(op->data, data_len, | |
d295e8e9 | 149 | ntohs(op->total_len)); |
064af421 BP |
150 | ds_put_cstr(string, packet); |
151 | free(packet); | |
152 | } | |
153 | } | |
154 | ||
d295e8e9 | 155 | static void ofp_print_port_name(struct ds *string, uint16_t port) |
064af421 BP |
156 | { |
157 | const char *name; | |
158 | switch (port) { | |
159 | case OFPP_IN_PORT: | |
160 | name = "IN_PORT"; | |
161 | break; | |
162 | case OFPP_TABLE: | |
163 | name = "TABLE"; | |
164 | break; | |
165 | case OFPP_NORMAL: | |
166 | name = "NORMAL"; | |
167 | break; | |
168 | case OFPP_FLOOD: | |
169 | name = "FLOOD"; | |
170 | break; | |
171 | case OFPP_ALL: | |
172 | name = "ALL"; | |
173 | break; | |
174 | case OFPP_CONTROLLER: | |
175 | name = "CONTROLLER"; | |
176 | break; | |
177 | case OFPP_LOCAL: | |
178 | name = "LOCAL"; | |
179 | break; | |
180 | case OFPP_NONE: | |
181 | name = "NONE"; | |
182 | break; | |
183 | default: | |
184 | ds_put_format(string, "%"PRIu16, port); | |
185 | return; | |
186 | } | |
187 | ds_put_cstr(string, name); | |
188 | } | |
189 | ||
f393f81e | 190 | |
96fc46e8 BP |
191 | static void |
192 | print_note(struct ds *string, const struct nx_action_note *nan) | |
193 | { | |
194 | size_t len; | |
195 | size_t i; | |
196 | ||
197 | ds_put_cstr(string, "note:"); | |
198 | len = ntohs(nan->len) - offsetof(struct nx_action_note, note); | |
199 | for (i = 0; i < len; i++) { | |
200 | if (i) { | |
201 | ds_put_char(string, '.'); | |
202 | } | |
203 | ds_put_format(string, "%02"PRIx8, nan->note[i]); | |
204 | } | |
205 | } | |
206 | ||
064af421 | 207 | static void |
38f2e360 BP |
208 | ofp_print_action(struct ds *s, const union ofp_action *a, |
209 | enum ofputil_action_code code) | |
064af421 | 210 | { |
38f2e360 BP |
211 | const struct ofp_action_enqueue *oae; |
212 | const struct ofp_action_dl_addr *oada; | |
213 | const struct nx_action_set_tunnel64 *nast64; | |
214 | const struct nx_action_set_tunnel *nast; | |
215 | const struct nx_action_set_queue *nasq; | |
216 | const struct nx_action_resubmit *nar; | |
217 | const struct nx_action_reg_move *move; | |
218 | const struct nx_action_reg_load *load; | |
219 | const struct nx_action_multipath *nam; | |
220 | const struct nx_action_autopath *naa; | |
221 | uint16_t port; | |
222 | ||
223 | switch (code) { | |
224 | case OFPUTIL_OFPAT_OUTPUT: | |
225 | port = ntohs(a->output.port); | |
226 | if (port < OFPP_MAX) { | |
227 | ds_put_format(s, "output:%"PRIu16, port); | |
228 | } else { | |
229 | ofp_print_port_name(s, port); | |
230 | if (port == OFPP_CONTROLLER) { | |
231 | if (a->output.max_len != htons(0)) { | |
232 | ds_put_format(s, ":%"PRIu16, ntohs(a->output.max_len)); | |
233 | } else { | |
234 | ds_put_cstr(s, ":all"); | |
235 | } | |
236 | } | |
237 | } | |
238 | break; | |
f393f81e | 239 | |
38f2e360 BP |
240 | case OFPUTIL_OFPAT_ENQUEUE: |
241 | oae = (const struct ofp_action_enqueue *) a; | |
242 | ds_put_format(s, "enqueue:"); | |
243 | ofp_print_port_name(s, ntohs(oae->port)); | |
244 | ds_put_format(s, "q%"PRIu32, ntohl(oae->queue_id)); | |
245 | break; | |
e41a9130 | 246 | |
38f2e360 BP |
247 | case OFPUTIL_OFPAT_SET_VLAN_VID: |
248 | ds_put_format(s, "mod_vlan_vid:%"PRIu16, | |
249 | ntohs(a->vlan_vid.vlan_vid)); | |
250 | break; | |
eedc0097 | 251 | |
38f2e360 BP |
252 | case OFPUTIL_OFPAT_SET_VLAN_PCP: |
253 | ds_put_format(s, "mod_vlan_pcp:%"PRIu8, a->vlan_pcp.vlan_pcp); | |
254 | break; | |
96fc46e8 | 255 | |
38f2e360 BP |
256 | case OFPUTIL_OFPAT_STRIP_VLAN: |
257 | ds_put_cstr(s, "strip_vlan"); | |
258 | break; | |
064af421 | 259 | |
38f2e360 BP |
260 | case OFPUTIL_OFPAT_SET_DL_SRC: |
261 | oada = (const struct ofp_action_dl_addr *) a; | |
262 | ds_put_format(s, "mod_dl_src:"ETH_ADDR_FMT, | |
263 | ETH_ADDR_ARGS(oada->dl_addr)); | |
264 | break; | |
064af421 | 265 | |
38f2e360 BP |
266 | case OFPUTIL_OFPAT_SET_DL_DST: |
267 | oada = (const struct ofp_action_dl_addr *) a; | |
268 | ds_put_format(s, "mod_dl_dst:"ETH_ADDR_FMT, | |
269 | ETH_ADDR_ARGS(oada->dl_addr)); | |
270 | break; | |
064af421 | 271 | |
38f2e360 BP |
272 | case OFPUTIL_OFPAT_SET_NW_SRC: |
273 | ds_put_format(s, "mod_nw_src:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr)); | |
274 | break; | |
d295e8e9 | 275 | |
38f2e360 BP |
276 | case OFPUTIL_OFPAT_SET_NW_DST: |
277 | ds_put_format(s, "mod_nw_dst:"IP_FMT, IP_ARGS(&a->nw_addr.nw_addr)); | |
064af421 | 278 | break; |
064af421 | 279 | |
38f2e360 BP |
280 | case OFPUTIL_OFPAT_SET_NW_TOS: |
281 | ds_put_format(s, "mod_nw_tos:%d", a->nw_tos.nw_tos); | |
c4d279ab | 282 | break; |
c4d279ab | 283 | |
38f2e360 BP |
284 | case OFPUTIL_OFPAT_SET_TP_SRC: |
285 | ds_put_format(s, "mod_tp_src:%d", ntohs(a->tp_port.tp_port)); | |
064af421 | 286 | break; |
064af421 | 287 | |
38f2e360 BP |
288 | case OFPUTIL_OFPAT_SET_TP_DST: |
289 | ds_put_format(s, "mod_tp_dst:%d", ntohs(a->tp_port.tp_port)); | |
064af421 | 290 | break; |
064af421 | 291 | |
38f2e360 BP |
292 | case OFPUTIL_NXAST_RESUBMIT: |
293 | nar = (struct nx_action_resubmit *)a; | |
294 | ds_put_format(s, "resubmit:"); | |
295 | ofp_print_port_name(s, ntohs(nar->in_port)); | |
064af421 BP |
296 | break; |
297 | ||
38f2e360 BP |
298 | case OFPUTIL_NXAST_SET_TUNNEL: |
299 | nast = (struct nx_action_set_tunnel *)a; | |
300 | ds_put_format(s, "set_tunnel:%#"PRIx32, ntohl(nast->tun_id)); | |
064af421 | 301 | break; |
064af421 | 302 | |
38f2e360 BP |
303 | case OFPUTIL_NXAST_SET_QUEUE: |
304 | nasq = (struct nx_action_set_queue *)a; | |
305 | ds_put_format(s, "set_queue:%u", ntohl(nasq->queue_id)); | |
064af421 | 306 | break; |
064af421 | 307 | |
38f2e360 BP |
308 | case OFPUTIL_NXAST_POP_QUEUE: |
309 | ds_put_cstr(s, "pop_queue"); | |
064af421 | 310 | break; |
064af421 | 311 | |
38f2e360 BP |
312 | case OFPUTIL_NXAST_NOTE: |
313 | print_note(s, (const struct nx_action_note *) a); | |
064af421 | 314 | break; |
064af421 | 315 | |
38f2e360 BP |
316 | case OFPUTIL_NXAST_REG_MOVE: |
317 | move = (const struct nx_action_reg_move *) a; | |
318 | nxm_format_reg_move(move, s); | |
959a2ecd | 319 | break; |
959a2ecd | 320 | |
38f2e360 BP |
321 | case OFPUTIL_NXAST_REG_LOAD: |
322 | load = (const struct nx_action_reg_load *) a; | |
323 | nxm_format_reg_load(load, s); | |
064af421 | 324 | break; |
064af421 | 325 | |
38f2e360 BP |
326 | case OFPUTIL_NXAST_SET_TUNNEL64: |
327 | nast64 = (const struct nx_action_set_tunnel64 *) a; | |
328 | ds_put_format(s, "set_tunnel64:%#"PRIx64, | |
329 | ntohll(nast64->tun_id)); | |
064af421 | 330 | break; |
064af421 | 331 | |
38f2e360 BP |
332 | case OFPUTIL_NXAST_MULTIPATH: |
333 | nam = (const struct nx_action_multipath *) a; | |
334 | multipath_format(nam, s); | |
335 | break; | |
336 | ||
337 | case OFPUTIL_NXAST_AUTOPATH: | |
338 | naa = (const struct nx_action_autopath *)a; | |
339 | ds_put_format(s, "autopath(%u,", ntohl(naa->id)); | |
340 | nxm_format_field_bits(s, ntohl(naa->dst), | |
341 | nxm_decode_ofs(naa->ofs_nbits), | |
342 | nxm_decode_n_bits(naa->ofs_nbits)); | |
343 | ds_put_char(s, ')'); | |
064af421 | 344 | break; |
064af421 | 345 | |
daff3353 | 346 | case OFPUTIL_NXAST_BUNDLE: |
a368bb53 | 347 | case OFPUTIL_NXAST_BUNDLE_LOAD: |
daff3353 EJ |
348 | bundle_format((const struct nx_action_bundle *) a, s); |
349 | break; | |
350 | ||
064af421 | 351 | default: |
064af421 BP |
352 | break; |
353 | } | |
064af421 BP |
354 | } |
355 | ||
d295e8e9 | 356 | void |
b4b8c781 BP |
357 | ofp_print_actions(struct ds *string, const union ofp_action *actions, |
358 | size_t n_actions) | |
064af421 | 359 | { |
b4b8c781 BP |
360 | const union ofp_action *a; |
361 | size_t left; | |
064af421 BP |
362 | |
363 | ds_put_cstr(string, "actions="); | |
b4b8c781 | 364 | if (!n_actions) { |
064af421 BP |
365 | ds_put_cstr(string, "drop"); |
366 | } | |
38f2e360 | 367 | |
b4b8c781 | 368 | OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { |
38f2e360 BP |
369 | int code = ofputil_decode_action(a); |
370 | if (code >= 0) { | |
371 | if (a != actions) { | |
372 | ds_put_cstr(string, ","); | |
373 | } | |
374 | ofp_print_action(string, a, code); | |
375 | } else { | |
376 | ofp_print_error(string, -code); | |
064af421 | 377 | } |
b4b8c781 BP |
378 | } |
379 | if (left > 0) { | |
380 | ds_put_format(string, " ***%zu leftover bytes following actions", | |
381 | left * sizeof *a); | |
064af421 BP |
382 | } |
383 | } | |
384 | ||
d1e2cf21 BP |
385 | static void |
386 | ofp_print_packet_out(struct ds *string, const struct ofp_packet_out *opo, | |
387 | int verbosity) | |
064af421 | 388 | { |
d1e2cf21 | 389 | size_t len = ntohs(opo->header.length); |
064af421 BP |
390 | size_t actions_len = ntohs(opo->actions_len); |
391 | ||
392 | ds_put_cstr(string, " in_port="); | |
393 | ofp_print_port_name(string, ntohs(opo->in_port)); | |
394 | ||
395 | ds_put_format(string, " actions_len=%zu ", actions_len); | |
396 | if (actions_len > (ntohs(opo->header.length) - sizeof *opo)) { | |
397 | ds_put_format(string, "***packet too short for action length***\n"); | |
398 | return; | |
399 | } | |
b4b8c781 BP |
400 | if (actions_len % sizeof(union ofp_action)) { |
401 | ds_put_format(string, "***action length not a multiple of %zu***\n", | |
402 | sizeof(union ofp_action)); | |
403 | } | |
404 | ofp_print_actions(string, (const union ofp_action *) opo->actions, | |
405 | actions_len / sizeof(union ofp_action)); | |
064af421 BP |
406 | |
407 | if (ntohl(opo->buffer_id) == UINT32_MAX) { | |
408 | int data_len = len - sizeof *opo - actions_len; | |
409 | ds_put_format(string, " data_len=%d", data_len); | |
410 | if (verbosity > 0 && len > sizeof *opo) { | |
411 | char *packet = ofp_packet_to_string( | |
412 | (uint8_t *)opo->actions + actions_len, data_len, data_len); | |
413 | ds_put_char(string, '\n'); | |
414 | ds_put_cstr(string, packet); | |
415 | free(packet); | |
416 | } | |
417 | } else { | |
418 | ds_put_format(string, " buffer=0x%08"PRIx32, ntohl(opo->buffer_id)); | |
419 | } | |
420 | ds_put_char(string, '\n'); | |
421 | } | |
422 | ||
423 | /* qsort comparison function. */ | |
424 | static int | |
425 | compare_ports(const void *a_, const void *b_) | |
426 | { | |
427 | const struct ofp_phy_port *a = a_; | |
428 | const struct ofp_phy_port *b = b_; | |
429 | uint16_t ap = ntohs(a->port_no); | |
430 | uint16_t bp = ntohs(b->port_no); | |
431 | ||
432 | return ap < bp ? -1 : ap > bp; | |
433 | } | |
434 | ||
435 | static void ofp_print_port_features(struct ds *string, uint32_t features) | |
436 | { | |
437 | if (features == 0) { | |
438 | ds_put_cstr(string, "Unsupported\n"); | |
439 | return; | |
440 | } | |
441 | if (features & OFPPF_10MB_HD) { | |
442 | ds_put_cstr(string, "10MB-HD "); | |
443 | } | |
444 | if (features & OFPPF_10MB_FD) { | |
445 | ds_put_cstr(string, "10MB-FD "); | |
446 | } | |
447 | if (features & OFPPF_100MB_HD) { | |
448 | ds_put_cstr(string, "100MB-HD "); | |
449 | } | |
450 | if (features & OFPPF_100MB_FD) { | |
451 | ds_put_cstr(string, "100MB-FD "); | |
452 | } | |
453 | if (features & OFPPF_1GB_HD) { | |
454 | ds_put_cstr(string, "1GB-HD "); | |
455 | } | |
456 | if (features & OFPPF_1GB_FD) { | |
457 | ds_put_cstr(string, "1GB-FD "); | |
458 | } | |
459 | if (features & OFPPF_10GB_FD) { | |
460 | ds_put_cstr(string, "10GB-FD "); | |
461 | } | |
462 | if (features & OFPPF_COPPER) { | |
463 | ds_put_cstr(string, "COPPER "); | |
464 | } | |
465 | if (features & OFPPF_FIBER) { | |
466 | ds_put_cstr(string, "FIBER "); | |
467 | } | |
468 | if (features & OFPPF_AUTONEG) { | |
469 | ds_put_cstr(string, "AUTO_NEG "); | |
470 | } | |
471 | if (features & OFPPF_PAUSE) { | |
472 | ds_put_cstr(string, "AUTO_PAUSE "); | |
473 | } | |
474 | if (features & OFPPF_PAUSE_ASYM) { | |
475 | ds_put_cstr(string, "AUTO_PAUSE_ASYM "); | |
476 | } | |
477 | ds_put_char(string, '\n'); | |
478 | } | |
479 | ||
480 | static void | |
481 | ofp_print_phy_port(struct ds *string, const struct ofp_phy_port *port) | |
482 | { | |
0b61210e | 483 | char name[OFP_MAX_PORT_NAME_LEN]; |
064af421 BP |
484 | int j; |
485 | ||
486 | memcpy(name, port->name, sizeof name); | |
487 | for (j = 0; j < sizeof name - 1; j++) { | |
858f2852 | 488 | if (!isprint((unsigned char) name[j])) { |
064af421 BP |
489 | break; |
490 | } | |
491 | } | |
492 | name[j] = '\0'; | |
493 | ||
494 | ds_put_char(string, ' '); | |
495 | ofp_print_port_name(string, ntohs(port->port_no)); | |
496 | ds_put_format(string, "(%s): addr:"ETH_ADDR_FMT", config: %#x, state:%#x\n", | |
497 | name, ETH_ADDR_ARGS(port->hw_addr), ntohl(port->config), | |
498 | ntohl(port->state)); | |
499 | if (port->curr) { | |
500 | ds_put_format(string, " current: "); | |
501 | ofp_print_port_features(string, ntohl(port->curr)); | |
502 | } | |
503 | if (port->advertised) { | |
504 | ds_put_format(string, " advertised: "); | |
505 | ofp_print_port_features(string, ntohl(port->advertised)); | |
506 | } | |
507 | if (port->supported) { | |
508 | ds_put_format(string, " supported: "); | |
509 | ofp_print_port_features(string, ntohl(port->supported)); | |
510 | } | |
511 | if (port->peer) { | |
512 | ds_put_format(string, " peer: "); | |
513 | ofp_print_port_features(string, ntohl(port->peer)); | |
514 | } | |
515 | } | |
516 | ||
064af421 | 517 | static void |
d1e2cf21 BP |
518 | ofp_print_switch_features(struct ds *string, |
519 | const struct ofp_switch_features *osf) | |
064af421 | 520 | { |
d1e2cf21 | 521 | size_t len = ntohs(osf->header.length); |
064af421 BP |
522 | struct ofp_phy_port *port_list; |
523 | int n_ports; | |
524 | int i; | |
525 | ||
d295e8e9 | 526 | ds_put_format(string, " ver:0x%x, dpid:%016"PRIx64"\n", |
064af421 BP |
527 | osf->header.version, ntohll(osf->datapath_id)); |
528 | ds_put_format(string, "n_tables:%d, n_buffers:%d\n", osf->n_tables, | |
529 | ntohl(osf->n_buffers)); | |
530 | ds_put_format(string, "features: capabilities:%#x, actions:%#x\n", | |
531 | ntohl(osf->capabilities), ntohl(osf->actions)); | |
532 | ||
064af421 BP |
533 | n_ports = (len - sizeof *osf) / sizeof *osf->ports; |
534 | ||
d295e8e9 | 535 | port_list = xmemdup(osf->ports, len - sizeof *osf); |
064af421 BP |
536 | qsort(port_list, n_ports, sizeof *port_list, compare_ports); |
537 | for (i = 0; i < n_ports; i++) { | |
538 | ofp_print_phy_port(string, &port_list[i]); | |
539 | } | |
540 | free(port_list); | |
541 | } | |
542 | ||
064af421 | 543 | static void |
d1e2cf21 | 544 | ofp_print_switch_config(struct ds *string, const struct ofp_switch_config *osc) |
064af421 | 545 | { |
064af421 BP |
546 | uint16_t flags; |
547 | ||
548 | flags = ntohs(osc->flags); | |
3b62feba BP |
549 | |
550 | ds_put_cstr(string, " frags="); | |
551 | switch (flags & OFPC_FRAG_MASK) { | |
552 | case OFPC_FRAG_NORMAL: | |
553 | ds_put_cstr(string, "normal"); | |
554 | flags &= ~OFPC_FRAG_MASK; | |
555 | break; | |
556 | case OFPC_FRAG_DROP: | |
557 | ds_put_cstr(string, "drop"); | |
558 | flags &= ~OFPC_FRAG_MASK; | |
559 | break; | |
560 | case OFPC_FRAG_REASM: | |
561 | ds_put_cstr(string, "reassemble"); | |
562 | flags &= ~OFPC_FRAG_MASK; | |
563 | break; | |
564 | } | |
064af421 BP |
565 | if (flags) { |
566 | ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", flags); | |
567 | } | |
568 | ||
569 | ds_put_format(string, " miss_send_len=%"PRIu16"\n", ntohs(osc->miss_send_len)); | |
570 | } | |
571 | ||
572 | static void print_wild(struct ds *string, const char *leader, int is_wild, | |
d295e8e9 | 573 | int verbosity, const char *format, ...) |
064af421 BP |
574 | __attribute__((format(printf, 5, 6))); |
575 | ||
576 | static void print_wild(struct ds *string, const char *leader, int is_wild, | |
d295e8e9 | 577 | int verbosity, const char *format, ...) |
064af421 BP |
578 | { |
579 | if (is_wild && verbosity < 2) { | |
580 | return; | |
581 | } | |
582 | ds_put_cstr(string, leader); | |
583 | if (!is_wild) { | |
584 | va_list args; | |
585 | ||
586 | va_start(args, format); | |
587 | ds_put_format_valist(string, format, args); | |
588 | va_end(args); | |
589 | } else { | |
590 | ds_put_char(string, '*'); | |
591 | } | |
592 | ds_put_char(string, ','); | |
593 | } | |
594 | ||
595 | static void | |
dbba996b | 596 | print_ip_netmask(struct ds *string, const char *leader, ovs_be32 ip, |
064af421 BP |
597 | uint32_t wild_bits, int verbosity) |
598 | { | |
599 | if (wild_bits >= 32 && verbosity < 2) { | |
600 | return; | |
601 | } | |
602 | ds_put_cstr(string, leader); | |
603 | if (wild_bits < 32) { | |
604 | ds_put_format(string, IP_FMT, IP_ARGS(&ip)); | |
605 | if (wild_bits) { | |
606 | ds_put_format(string, "/%d", 32 - wild_bits); | |
607 | } | |
608 | } else { | |
609 | ds_put_char(string, '*'); | |
610 | } | |
611 | ds_put_char(string, ','); | |
612 | } | |
613 | ||
4f2cad2c | 614 | void |
064af421 BP |
615 | ofp_print_match(struct ds *f, const struct ofp_match *om, int verbosity) |
616 | { | |
617 | char *s = ofp_match_to_string(om, verbosity); | |
618 | ds_put_cstr(f, s); | |
619 | free(s); | |
620 | } | |
621 | ||
622 | char * | |
623 | ofp_match_to_string(const struct ofp_match *om, int verbosity) | |
624 | { | |
625 | struct ds f = DS_EMPTY_INITIALIZER; | |
626 | uint32_t w = ntohl(om->wildcards); | |
627 | bool skip_type = false; | |
628 | bool skip_proto = false; | |
629 | ||
630 | if (!(w & OFPFW_DL_TYPE)) { | |
631 | skip_type = true; | |
632 | if (om->dl_type == htons(ETH_TYPE_IP)) { | |
633 | if (!(w & OFPFW_NW_PROTO)) { | |
634 | skip_proto = true; | |
6767a2cc | 635 | if (om->nw_proto == IPPROTO_ICMP) { |
064af421 | 636 | ds_put_cstr(&f, "icmp,"); |
6767a2cc | 637 | } else if (om->nw_proto == IPPROTO_TCP) { |
064af421 | 638 | ds_put_cstr(&f, "tcp,"); |
6767a2cc | 639 | } else if (om->nw_proto == IPPROTO_UDP) { |
064af421 BP |
640 | ds_put_cstr(&f, "udp,"); |
641 | } else { | |
642 | ds_put_cstr(&f, "ip,"); | |
643 | skip_proto = false; | |
644 | } | |
645 | } else { | |
646 | ds_put_cstr(&f, "ip,"); | |
647 | } | |
648 | } else if (om->dl_type == htons(ETH_TYPE_ARP)) { | |
649 | ds_put_cstr(&f, "arp,"); | |
650 | } else { | |
651 | skip_type = false; | |
652 | } | |
653 | } | |
654 | print_wild(&f, "in_port=", w & OFPFW_IN_PORT, verbosity, | |
655 | "%d", ntohs(om->in_port)); | |
656 | print_wild(&f, "dl_vlan=", w & OFPFW_DL_VLAN, verbosity, | |
6a1f89c8 | 657 | "%d", ntohs(om->dl_vlan)); |
959a2ecd JP |
658 | print_wild(&f, "dl_vlan_pcp=", w & OFPFW_DL_VLAN_PCP, verbosity, |
659 | "%d", om->dl_vlan_pcp); | |
064af421 BP |
660 | print_wild(&f, "dl_src=", w & OFPFW_DL_SRC, verbosity, |
661 | ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_src)); | |
662 | print_wild(&f, "dl_dst=", w & OFPFW_DL_DST, verbosity, | |
663 | ETH_ADDR_FMT, ETH_ADDR_ARGS(om->dl_dst)); | |
664 | if (!skip_type) { | |
665 | print_wild(&f, "dl_type=", w & OFPFW_DL_TYPE, verbosity, | |
666 | "0x%04x", ntohs(om->dl_type)); | |
667 | } | |
668 | print_ip_netmask(&f, "nw_src=", om->nw_src, | |
669 | (w & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT, verbosity); | |
670 | print_ip_netmask(&f, "nw_dst=", om->nw_dst, | |
671 | (w & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT, verbosity); | |
672 | if (!skip_proto) { | |
fb892732 | 673 | if (om->dl_type == htons(ETH_TYPE_ARP)) { |
fb115f91 | 674 | print_wild(&f, "arp_op=", w & OFPFW_NW_PROTO, verbosity, |
fb892732 JP |
675 | "%u", om->nw_proto); |
676 | } else { | |
677 | print_wild(&f, "nw_proto=", w & OFPFW_NW_PROTO, verbosity, | |
678 | "%u", om->nw_proto); | |
679 | } | |
064af421 | 680 | } |
1a960c80 RL |
681 | print_wild(&f, "nw_tos=", w & OFPFW_NW_TOS, verbosity, |
682 | "%u", om->nw_tos); | |
6767a2cc | 683 | if (om->nw_proto == IPPROTO_ICMP) { |
064af421 BP |
684 | print_wild(&f, "icmp_type=", w & OFPFW_ICMP_TYPE, verbosity, |
685 | "%d", ntohs(om->icmp_type)); | |
686 | print_wild(&f, "icmp_code=", w & OFPFW_ICMP_CODE, verbosity, | |
687 | "%d", ntohs(om->icmp_code)); | |
688 | } else { | |
689 | print_wild(&f, "tp_src=", w & OFPFW_TP_SRC, verbosity, | |
690 | "%d", ntohs(om->tp_src)); | |
691 | print_wild(&f, "tp_dst=", w & OFPFW_TP_DST, verbosity, | |
692 | "%d", ntohs(om->tp_dst)); | |
693 | } | |
7fa91113 BP |
694 | if (ds_last(&f) == ',') { |
695 | f.length--; | |
696 | } | |
064af421 BP |
697 | return ds_cstr(&f); |
698 | } | |
699 | ||
064af421 | 700 | static void |
7fa91113 BP |
701 | ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, |
702 | enum ofputil_msg_code code, int verbosity) | |
064af421 | 703 | { |
7fa91113 | 704 | struct flow_mod fm; |
f904747b | 705 | bool need_priority; |
7fa91113 | 706 | int error; |
064af421 | 707 | |
00794817 | 708 | error = ofputil_decode_flow_mod(&fm, oh, true); |
7fa91113 BP |
709 | if (error) { |
710 | ofp_print_error(s, error); | |
711 | return; | |
3ff4f871 BP |
712 | } |
713 | ||
7fa91113 BP |
714 | ds_put_char(s, ' '); |
715 | switch (fm.command) { | |
064af421 | 716 | case OFPFC_ADD: |
7fa91113 | 717 | ds_put_cstr(s, "ADD"); |
064af421 BP |
718 | break; |
719 | case OFPFC_MODIFY: | |
7fa91113 | 720 | ds_put_cstr(s, "MOD"); |
064af421 BP |
721 | break; |
722 | case OFPFC_MODIFY_STRICT: | |
7fa91113 | 723 | ds_put_cstr(s, "MOD_STRICT"); |
064af421 BP |
724 | break; |
725 | case OFPFC_DELETE: | |
7fa91113 | 726 | ds_put_cstr(s, "DEL"); |
064af421 BP |
727 | break; |
728 | case OFPFC_DELETE_STRICT: | |
7fa91113 | 729 | ds_put_cstr(s, "DEL_STRICT"); |
064af421 BP |
730 | break; |
731 | default: | |
7fa91113 BP |
732 | ds_put_format(s, "cmd:%d", fm.command); |
733 | } | |
6c1491fb | 734 | if (fm.table_id != 0) { |
e896c2d4 | 735 | ds_put_format(s, " table:%d", fm.table_id); |
6c1491fb | 736 | } |
7fa91113 BP |
737 | |
738 | ds_put_char(s, ' '); | |
739 | if (verbosity >= 3 && code == OFPUTIL_OFPT_FLOW_MOD) { | |
740 | const struct ofp_flow_mod *ofm = (const struct ofp_flow_mod *) oh; | |
7fa91113 | 741 | ofp_print_match(s, &ofm->match, verbosity); |
f904747b BP |
742 | |
743 | /* ofp_print_match() doesn't print priority. */ | |
744 | need_priority = true; | |
7fa91113 BP |
745 | } else if (verbosity >= 3 && code == OFPUTIL_NXT_FLOW_MOD) { |
746 | const struct nx_flow_mod *nfm = (const struct nx_flow_mod *) oh; | |
747 | const void *nxm = nfm + 1; | |
f904747b BP |
748 | char *nxm_s; |
749 | ||
750 | nxm_s = nx_match_to_string(nxm, ntohs(nfm->match_len)); | |
7fa91113 BP |
751 | ds_put_cstr(s, nxm_s); |
752 | free(nxm_s); | |
f904747b BP |
753 | |
754 | /* nx_match_to_string() doesn't print priority. */ | |
755 | need_priority = true; | |
7fa91113 BP |
756 | } else { |
757 | cls_rule_format(&fm.cr, s); | |
f904747b BP |
758 | |
759 | /* cls_rule_format() does print priority. */ | |
760 | need_priority = false; | |
3ff4f871 | 761 | } |
7fa91113 BP |
762 | |
763 | if (ds_last(s) != ' ') { | |
764 | ds_put_char(s, ' '); | |
3ff4f871 | 765 | } |
7fa91113 BP |
766 | if (fm.cookie != htonll(0)) { |
767 | ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.cookie)); | |
3ff4f871 | 768 | } |
7fa91113 BP |
769 | if (fm.idle_timeout != OFP_FLOW_PERMANENT) { |
770 | ds_put_format(s, "idle:%"PRIu16" ", fm.idle_timeout); | |
064af421 | 771 | } |
7fa91113 BP |
772 | if (fm.hard_timeout != OFP_FLOW_PERMANENT) { |
773 | ds_put_format(s, "hard:%"PRIu16" ", fm.hard_timeout); | |
3ff4f871 | 774 | } |
f904747b | 775 | if (fm.cr.priority != OFP_DEFAULT_PRIORITY && need_priority) { |
7fa91113 | 776 | ds_put_format(s, "pri:%"PRIu16" ", fm.cr.priority); |
3ff4f871 | 777 | } |
7fa91113 BP |
778 | if (fm.buffer_id != UINT32_MAX) { |
779 | ds_put_format(s, "buf:0x%"PRIx32" ", fm.buffer_id); | |
3ff4f871 | 780 | } |
7fa91113 BP |
781 | if (fm.flags != 0) { |
782 | ds_put_format(s, "flags:0x%"PRIx16" ", fm.flags); | |
783 | } | |
784 | ||
b4b8c781 | 785 | ofp_print_actions(s, fm.actions, fm.n_actions); |
064af421 BP |
786 | } |
787 | ||
09862ec6 BP |
788 | static void |
789 | ofp_print_duration(struct ds *string, unsigned int sec, unsigned int nsec) | |
790 | { | |
791 | ds_put_format(string, "%u", sec); | |
792 | if (nsec > 0) { | |
793 | ds_put_format(string, ".%09u", nsec); | |
794 | while (string->string[string->length - 1] == '0') { | |
795 | string->length--; | |
796 | } | |
797 | } | |
798 | ds_put_char(string, 's'); | |
799 | } | |
800 | ||
064af421 | 801 | static void |
9b045a0c | 802 | ofp_print_flow_removed(struct ds *string, const struct ofp_header *oh) |
064af421 | 803 | { |
9b045a0c BP |
804 | struct ofputil_flow_removed fr; |
805 | int error; | |
806 | ||
b78f6b77 | 807 | error = ofputil_decode_flow_removed(&fr, oh); |
9b045a0c BP |
808 | if (error) { |
809 | ofp_print_error(string, error); | |
810 | return; | |
811 | } | |
812 | ||
fbd76b2e | 813 | ds_put_char(string, ' '); |
9b045a0c BP |
814 | cls_rule_format(&fr.rule, string); |
815 | ||
064af421 | 816 | ds_put_cstr(string, " reason="); |
9b045a0c | 817 | switch (fr.reason) { |
ca069229 | 818 | case OFPRR_IDLE_TIMEOUT: |
064af421 BP |
819 | ds_put_cstr(string, "idle"); |
820 | break; | |
ca069229 | 821 | case OFPRR_HARD_TIMEOUT: |
064af421 BP |
822 | ds_put_cstr(string, "hard"); |
823 | break; | |
ca069229 JP |
824 | case OFPRR_DELETE: |
825 | ds_put_cstr(string, "delete"); | |
826 | break; | |
064af421 | 827 | default: |
9b045a0c | 828 | ds_put_format(string, "**%"PRIu8"**", fr.reason); |
064af421 BP |
829 | break; |
830 | } | |
3ff4f871 | 831 | |
9b045a0c BP |
832 | if (fr.cookie != htonll(0)) { |
833 | ds_put_format(string, " cookie:0x%"PRIx64, ntohll(fr.cookie)); | |
3ff4f871 | 834 | } |
09862ec6 | 835 | ds_put_cstr(string, " duration"); |
9b045a0c | 836 | ofp_print_duration(string, fr.duration_sec, fr.duration_nsec); |
09862ec6 | 837 | ds_put_format(string, " idle%"PRIu16" pkts%"PRIu64" bytes%"PRIu64"\n", |
9b045a0c | 838 | fr.idle_timeout, fr.packet_count, fr.byte_count); |
064af421 BP |
839 | } |
840 | ||
841 | static void | |
d1e2cf21 | 842 | ofp_print_port_mod(struct ds *string, const struct ofp_port_mod *opm) |
064af421 | 843 | { |
064af421 | 844 | ds_put_format(string, "port: %d: addr:"ETH_ADDR_FMT", config: %#x, mask:%#x\n", |
d295e8e9 | 845 | ntohs(opm->port_no), ETH_ADDR_ARGS(opm->hw_addr), |
064af421 BP |
846 | ntohl(opm->config), ntohl(opm->mask)); |
847 | ds_put_format(string, " advertise: "); | |
848 | if (opm->advertise) { | |
849 | ofp_print_port_features(string, ntohl(opm->advertise)); | |
850 | } else { | |
851 | ds_put_format(string, "UNCHANGED\n"); | |
852 | } | |
853 | } | |
854 | ||
7fa91113 BP |
855 | static void |
856 | ofp_print_error(struct ds *string, int error) | |
857 | { | |
7fa91113 BP |
858 | if (string->length) { |
859 | ds_put_char(string, ' '); | |
860 | } | |
dc4762ed BP |
861 | ds_put_cstr(string, "***decode error: "); |
862 | ofputil_format_error(string, error); | |
863 | ds_put_cstr(string, "***\n"); | |
f74be05a JP |
864 | } |
865 | ||
064af421 | 866 | static void |
d1e2cf21 | 867 | ofp_print_error_msg(struct ds *string, const struct ofp_error_msg *oem) |
064af421 | 868 | { |
d1e2cf21 | 869 | size_t len = ntohs(oem->header.length); |
dc4762ed BP |
870 | size_t payload_ofs, payload_len; |
871 | const void *payload; | |
872 | int error; | |
064af421 BP |
873 | char *s; |
874 | ||
dc4762ed BP |
875 | error = ofputil_decode_error_msg(&oem->header, &payload_ofs); |
876 | if (!is_ofp_error(error)) { | |
877 | ofp_print_error(string, error); | |
878 | ds_put_hex_dump(string, oem->data, len - sizeof *oem, 0, true); | |
879 | return; | |
f74be05a JP |
880 | } |
881 | ||
dc4762ed BP |
882 | ds_put_char(string, ' '); |
883 | ofputil_format_error(string, error); | |
884 | ds_put_char(string, '\n'); | |
064af421 | 885 | |
dc4762ed BP |
886 | payload = (const uint8_t *) oem + payload_ofs; |
887 | payload_len = len - payload_ofs; | |
888 | switch (get_ofp_err_type(error)) { | |
064af421 | 889 | case OFPET_HELLO_FAILED: |
dc4762ed | 890 | ds_put_printable(string, payload, payload_len); |
064af421 BP |
891 | break; |
892 | ||
893 | case OFPET_BAD_REQUEST: | |
dc4762ed | 894 | s = ofp_to_string(payload, payload_len, 1); |
064af421 BP |
895 | ds_put_cstr(string, s); |
896 | free(s); | |
897 | break; | |
898 | ||
899 | default: | |
dc4762ed | 900 | ds_put_hex_dump(string, payload, payload_len, 0, true); |
064af421 BP |
901 | break; |
902 | } | |
903 | } | |
904 | ||
064af421 | 905 | static void |
d1e2cf21 | 906 | ofp_print_port_status(struct ds *string, const struct ofp_port_status *ops) |
064af421 | 907 | { |
064af421 BP |
908 | if (ops->reason == OFPPR_ADD) { |
909 | ds_put_format(string, " ADD:"); | |
910 | } else if (ops->reason == OFPPR_DELETE) { | |
911 | ds_put_format(string, " DEL:"); | |
912 | } else if (ops->reason == OFPPR_MODIFY) { | |
913 | ds_put_format(string, " MOD:"); | |
914 | } | |
915 | ||
916 | ofp_print_phy_port(string, &ops->desc); | |
917 | } | |
918 | ||
919 | static void | |
63f2140a | 920 | ofp_print_ofpst_desc_reply(struct ds *string, const struct ofp_desc_stats *ods) |
064af421 | 921 | { |
fbd76b2e | 922 | ds_put_char(string, '\n'); |
d295e8e9 | 923 | ds_put_format(string, "Manufacturer: %.*s\n", |
dd70b475 JP |
924 | (int) sizeof ods->mfr_desc, ods->mfr_desc); |
925 | ds_put_format(string, "Hardware: %.*s\n", | |
926 | (int) sizeof ods->hw_desc, ods->hw_desc); | |
927 | ds_put_format(string, "Software: %.*s\n", | |
928 | (int) sizeof ods->sw_desc, ods->sw_desc); | |
929 | ds_put_format(string, "Serial Num: %.*s\n", | |
930 | (int) sizeof ods->serial_num, ods->serial_num); | |
931 | ds_put_format(string, "DP Description: %.*s\n", | |
932 | (int) sizeof ods->dp_desc, ods->dp_desc); | |
064af421 BP |
933 | } |
934 | ||
935 | static void | |
76c93b22 BP |
936 | ofp_print_flow_stats_request(struct ds *string, |
937 | const struct ofp_stats_msg *osm) | |
064af421 | 938 | { |
2af0c0ef BP |
939 | struct flow_stats_request fsr; |
940 | int error; | |
064af421 | 941 | |
76c93b22 | 942 | error = ofputil_decode_flow_stats_request(&fsr, &osm->header); |
2af0c0ef BP |
943 | if (error) { |
944 | ofp_print_error(string, error); | |
945 | return; | |
946 | } | |
947 | ||
948 | if (fsr.table_id != 0xff) { | |
e896c2d4 | 949 | ds_put_format(string, " table=%"PRIu8, fsr.table_id); |
064af421 BP |
950 | } |
951 | ||
2af0c0ef BP |
952 | if (fsr.out_port != OFPP_NONE) { |
953 | ds_put_cstr(string, " out_port="); | |
954 | ofp_print_port_name(string, fsr.out_port); | |
955 | } | |
956 | ||
54ae6fa8 BP |
957 | /* A flow stats request doesn't include a priority, but cls_rule_format() |
958 | * will print one unless it is OFP_DEFAULT_PRIORITY. */ | |
959 | fsr.match.priority = OFP_DEFAULT_PRIORITY; | |
960 | ||
2af0c0ef BP |
961 | ds_put_char(string, ' '); |
962 | cls_rule_format(&fsr.match, string); | |
064af421 BP |
963 | } |
964 | ||
965 | static void | |
4ffd1b43 | 966 | ofp_print_flow_stats_reply(struct ds *string, const struct ofp_header *oh) |
064af421 | 967 | { |
4ffd1b43 | 968 | struct ofpbuf b; |
064af421 | 969 | |
4ffd1b43 BP |
970 | ofpbuf_use_const(&b, oh, ntohs(oh->length)); |
971 | for (;;) { | |
972 | struct ofputil_flow_stats fs; | |
973 | int retval; | |
fab8fadb | 974 | |
b78f6b77 | 975 | retval = ofputil_decode_flow_stats_reply(&fs, &b); |
4ffd1b43 BP |
976 | if (retval) { |
977 | if (retval != EOF) { | |
978 | ds_put_cstr(string, " ***parse error***"); | |
064af421 BP |
979 | } |
980 | break; | |
981 | } | |
982 | ||
8961de6a BP |
983 | ds_put_char(string, '\n'); |
984 | ||
09862ec6 | 985 | ds_put_format(string, " cookie=0x%"PRIx64", duration=", |
4ffd1b43 BP |
986 | ntohll(fs.cookie)); |
987 | ofp_print_duration(string, fs.duration_sec, fs.duration_nsec); | |
e896c2d4 | 988 | ds_put_format(string, ", table=%"PRIu8", ", fs.table_id); |
4ffd1b43 BP |
989 | ds_put_format(string, "n_packets=%"PRIu64", ", fs.packet_count); |
990 | ds_put_format(string, "n_bytes=%"PRIu64", ", fs.byte_count); | |
991 | if (fs.idle_timeout != OFP_FLOW_PERMANENT) { | |
992 | ds_put_format(string, "idle_timeout=%"PRIu16",", fs.idle_timeout); | |
c6430da5 | 993 | } |
4ffd1b43 BP |
994 | if (fs.hard_timeout != OFP_FLOW_PERMANENT) { |
995 | ds_put_format(string, "hard_timeout=%"PRIu16",", fs.hard_timeout); | |
c6430da5 BP |
996 | } |
997 | ||
4ffd1b43 | 998 | cls_rule_format(&fs.rule, string); |
c6430da5 | 999 | ds_put_char(string, ' '); |
b4b8c781 | 1000 | ofp_print_actions(string, fs.actions, fs.n_actions); |
c6430da5 BP |
1001 | } |
1002 | } | |
1003 | ||
064af421 | 1004 | static void |
63f2140a BP |
1005 | ofp_print_ofpst_aggregate_reply(struct ds *string, |
1006 | const struct ofp_aggregate_stats_reply *asr) | |
064af421 | 1007 | { |
c4617b3c BP |
1008 | ds_put_format(string, " packet_count=%"PRIu64, |
1009 | ntohll(get_32aligned_be64(&asr->packet_count))); | |
1010 | ds_put_format(string, " byte_count=%"PRIu64, | |
1011 | ntohll(get_32aligned_be64(&asr->byte_count))); | |
064af421 BP |
1012 | ds_put_format(string, " flow_count=%"PRIu32, ntohl(asr->flow_count)); |
1013 | } | |
1014 | ||
a2ad9ecd BP |
1015 | static void |
1016 | ofp_print_nxst_aggregate_reply(struct ds *string, | |
1017 | const struct nx_aggregate_stats_reply *nasr) | |
1018 | { | |
675baf0c BP |
1019 | ds_put_format(string, " packet_count=%"PRIu64, ntohll(nasr->packet_count)); |
1020 | ds_put_format(string, " byte_count=%"PRIu64, ntohll(nasr->byte_count)); | |
1021 | ds_put_format(string, " flow_count=%"PRIu32, ntohl(nasr->flow_count)); | |
a2ad9ecd BP |
1022 | } |
1023 | ||
d295e8e9 | 1024 | static void print_port_stat(struct ds *string, const char *leader, |
c4617b3c | 1025 | const ovs_32aligned_be64 *statp, int more) |
064af421 | 1026 | { |
c4617b3c BP |
1027 | uint64_t stat = ntohll(get_32aligned_be64(statp)); |
1028 | ||
064af421 | 1029 | ds_put_cstr(string, leader); |
c4617b3c | 1030 | if (stat != UINT64_MAX) { |
064af421 BP |
1031 | ds_put_format(string, "%"PRIu64, stat); |
1032 | } else { | |
1033 | ds_put_char(string, '?'); | |
1034 | } | |
1035 | if (more) { | |
1036 | ds_put_cstr(string, ", "); | |
1037 | } else { | |
1038 | ds_put_cstr(string, "\n"); | |
1039 | } | |
1040 | } | |
1041 | ||
abaad8cf | 1042 | static void |
63f2140a BP |
1043 | ofp_print_ofpst_port_request(struct ds *string, |
1044 | const struct ofp_port_stats_request *psr) | |
abaad8cf | 1045 | { |
fbd76b2e | 1046 | ds_put_format(string, " port_no=%"PRIu16, ntohs(psr->port_no)); |
abaad8cf JP |
1047 | } |
1048 | ||
064af421 | 1049 | static void |
d1e2cf21 BP |
1050 | ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh, |
1051 | int verbosity) | |
064af421 | 1052 | { |
d1e2cf21 BP |
1053 | const struct ofp_port_stats *ps = ofputil_stats_body(oh); |
1054 | size_t n = ofputil_stats_body_len(oh) / sizeof *ps; | |
064af421 BP |
1055 | ds_put_format(string, " %zu ports\n", n); |
1056 | if (verbosity < 1) { | |
1057 | return; | |
1058 | } | |
1059 | ||
1060 | for (; n--; ps++) { | |
1061 | ds_put_format(string, " port %2"PRIu16": ", ntohs(ps->port_no)); | |
1062 | ||
1063 | ds_put_cstr(string, "rx "); | |
c4617b3c BP |
1064 | print_port_stat(string, "pkts=", &ps->rx_packets, 1); |
1065 | print_port_stat(string, "bytes=", &ps->rx_bytes, 1); | |
1066 | print_port_stat(string, "drop=", &ps->rx_dropped, 1); | |
1067 | print_port_stat(string, "errs=", &ps->rx_errors, 1); | |
1068 | print_port_stat(string, "frame=", &ps->rx_frame_err, 1); | |
1069 | print_port_stat(string, "over=", &ps->rx_over_err, 1); | |
1070 | print_port_stat(string, "crc=", &ps->rx_crc_err, 0); | |
064af421 BP |
1071 | |
1072 | ds_put_cstr(string, " tx "); | |
c4617b3c BP |
1073 | print_port_stat(string, "pkts=", &ps->tx_packets, 1); |
1074 | print_port_stat(string, "bytes=", &ps->tx_bytes, 1); | |
1075 | print_port_stat(string, "drop=", &ps->tx_dropped, 1); | |
1076 | print_port_stat(string, "errs=", &ps->tx_errors, 1); | |
1077 | print_port_stat(string, "coll=", &ps->collisions, 0); | |
064af421 BP |
1078 | } |
1079 | } | |
1080 | ||
1081 | static void | |
d1e2cf21 BP |
1082 | ofp_print_ofpst_table_reply(struct ds *string, const struct ofp_header *oh, |
1083 | int verbosity) | |
064af421 | 1084 | { |
d1e2cf21 BP |
1085 | const struct ofp_table_stats *ts = ofputil_stats_body(oh); |
1086 | size_t n = ofputil_stats_body_len(oh) / sizeof *ts; | |
064af421 BP |
1087 | ds_put_format(string, " %zu tables\n", n); |
1088 | if (verbosity < 1) { | |
1089 | return; | |
1090 | } | |
1091 | ||
1092 | for (; n--; ts++) { | |
1093 | char name[OFP_MAX_TABLE_NAME_LEN + 1]; | |
e868fb3d | 1094 | ovs_strlcpy(name, ts->name, sizeof name); |
064af421 BP |
1095 | |
1096 | ds_put_format(string, " %d: %-8s: ", ts->table_id, name); | |
1097 | ds_put_format(string, "wild=0x%05"PRIx32", ", ntohl(ts->wildcards)); | |
1098 | ds_put_format(string, "max=%6"PRIu32", ", ntohl(ts->max_entries)); | |
1099 | ds_put_format(string, "active=%"PRIu32"\n", ntohl(ts->active_count)); | |
1100 | ds_put_cstr(string, " "); | |
d295e8e9 | 1101 | ds_put_format(string, "lookup=%"PRIu64", ", |
c4617b3c | 1102 | ntohll(get_32aligned_be64(&ts->lookup_count))); |
064af421 | 1103 | ds_put_format(string, "matched=%"PRIu64"\n", |
c4617b3c | 1104 | ntohll(get_32aligned_be64(&ts->matched_count))); |
064af421 BP |
1105 | } |
1106 | } | |
1107 | ||
d2805da2 BP |
1108 | static void |
1109 | ofp_print_queue_name(struct ds *string, uint32_t queue_id) | |
1110 | { | |
1111 | if (queue_id == OFPQ_ALL) { | |
1112 | ds_put_cstr(string, "ALL"); | |
1113 | } else { | |
1114 | ds_put_format(string, "%"PRIu32, queue_id); | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | static void | |
63f2140a BP |
1119 | ofp_print_ofpst_queue_request(struct ds *string, |
1120 | const struct ofp_queue_stats_request *qsr) | |
d2805da2 | 1121 | { |
d2805da2 BP |
1122 | ds_put_cstr(string, "port="); |
1123 | ofp_print_port_name(string, ntohs(qsr->port_no)); | |
1124 | ||
1125 | ds_put_cstr(string, " queue="); | |
1126 | ofp_print_queue_name(string, ntohl(qsr->queue_id)); | |
1127 | } | |
1128 | ||
1129 | static void | |
d1e2cf21 BP |
1130 | ofp_print_ofpst_queue_reply(struct ds *string, const struct ofp_header *oh, |
1131 | int verbosity) | |
d2805da2 | 1132 | { |
d1e2cf21 BP |
1133 | const struct ofp_queue_stats *qs = ofputil_stats_body(oh); |
1134 | size_t n = ofputil_stats_body_len(oh) / sizeof *qs; | |
d2805da2 BP |
1135 | ds_put_format(string, " %zu queues\n", n); |
1136 | if (verbosity < 1) { | |
1137 | return; | |
1138 | } | |
1139 | ||
1140 | for (; n--; qs++) { | |
1141 | ds_put_cstr(string, " port "); | |
1142 | ofp_print_port_name(string, ntohs(qs->port_no)); | |
1143 | ds_put_cstr(string, " queue "); | |
1144 | ofp_print_queue_name(string, ntohl(qs->queue_id)); | |
1145 | ds_put_cstr(string, ": "); | |
1146 | ||
c4617b3c BP |
1147 | print_port_stat(string, "bytes=", &qs->tx_bytes, 1); |
1148 | print_port_stat(string, "pkts=", &qs->tx_packets, 1); | |
1149 | print_port_stat(string, "errors=", &qs->tx_errors, 0); | |
d2805da2 BP |
1150 | } |
1151 | } | |
1152 | ||
064af421 | 1153 | static void |
d1e2cf21 | 1154 | ofp_print_stats_request(struct ds *string, const struct ofp_header *oh) |
064af421 | 1155 | { |
28c8bad1 | 1156 | const struct ofp_stats_msg *srq = (const struct ofp_stats_msg *) oh; |
064af421 BP |
1157 | |
1158 | if (srq->flags) { | |
1159 | ds_put_format(string, " ***unknown flags 0x%04"PRIx16"***", | |
1160 | ntohs(srq->flags)); | |
1161 | } | |
064af421 BP |
1162 | } |
1163 | ||
1164 | static void | |
d1e2cf21 | 1165 | ofp_print_stats_reply(struct ds *string, const struct ofp_header *oh) |
064af421 | 1166 | { |
28c8bad1 | 1167 | const struct ofp_stats_msg *srp = (const struct ofp_stats_msg *) oh; |
064af421 | 1168 | |
d1e2cf21 | 1169 | if (srp->flags) { |
064af421 | 1170 | uint16_t flags = ntohs(srp->flags); |
d1e2cf21 BP |
1171 | |
1172 | ds_put_cstr(string, " flags="); | |
064af421 BP |
1173 | if (flags & OFPSF_REPLY_MORE) { |
1174 | ds_put_cstr(string, "[more]"); | |
1175 | flags &= ~OFPSF_REPLY_MORE; | |
1176 | } | |
1177 | if (flags) { | |
d1e2cf21 BP |
1178 | ds_put_format(string, "[***unknown flags 0x%04"PRIx16"***]", |
1179 | flags); | |
064af421 BP |
1180 | } |
1181 | } | |
064af421 BP |
1182 | } |
1183 | ||
1184 | static void | |
d1e2cf21 | 1185 | ofp_print_echo(struct ds *string, const struct ofp_header *oh, int verbosity) |
064af421 | 1186 | { |
d1e2cf21 | 1187 | size_t len = ntohs(oh->length); |
064af421 | 1188 | |
d1e2cf21 | 1189 | ds_put_format(string, " %zu bytes of payload\n", len - sizeof *oh); |
064af421 | 1190 | if (verbosity > 1) { |
d1e2cf21 | 1191 | ds_put_hex_dump(string, oh + 1, len - sizeof *oh, 0, true); |
064af421 BP |
1192 | } |
1193 | } | |
1194 | ||
61fe3a7b BP |
1195 | static void |
1196 | ofp_print_nxt_role_message(struct ds *string, | |
1197 | const struct nx_role_request *nrr) | |
1198 | { | |
1199 | unsigned int role = ntohl(nrr->role); | |
1200 | ||
1201 | ds_put_cstr(string, " role="); | |
1202 | if (role == NX_ROLE_OTHER) { | |
1203 | ds_put_cstr(string, "other"); | |
1204 | } else if (role == NX_ROLE_MASTER) { | |
1205 | ds_put_cstr(string, "master"); | |
1206 | } else if (role == NX_ROLE_SLAVE) { | |
1207 | ds_put_cstr(string, "slave"); | |
1208 | } else { | |
1209 | ds_put_format(string, "%u", role); | |
1210 | } | |
1211 | } | |
1212 | ||
6c1491fb BP |
1213 | static void |
1214 | ofp_print_nxt_flow_mod_table_id(struct ds *string, | |
1215 | const struct nxt_flow_mod_table_id *nfmti) | |
1216 | { | |
1217 | ds_put_format(string, " %s", nfmti->set ? "enable" : "disable"); | |
1218 | } | |
1219 | ||
7fa91113 BP |
1220 | static void |
1221 | ofp_print_nxt_set_flow_format(struct ds *string, | |
1222 | const struct nxt_set_flow_format *nsff) | |
1223 | { | |
1224 | uint32_t format = ntohl(nsff->format); | |
1225 | ||
1226 | ds_put_cstr(string, " format="); | |
1227 | if (ofputil_flow_format_is_valid(format)) { | |
1228 | ds_put_cstr(string, ofputil_flow_format_to_string(format)); | |
1229 | } else { | |
1230 | ds_put_format(string, "%"PRIu32, format); | |
1231 | } | |
1232 | } | |
1233 | ||
d1e2cf21 BP |
1234 | static void |
1235 | ofp_to_string__(const struct ofp_header *oh, | |
1236 | const struct ofputil_msg_type *type, struct ds *string, | |
1237 | int verbosity) | |
1238 | { | |
7fa91113 | 1239 | enum ofputil_msg_code code; |
d1e2cf21 BP |
1240 | const void *msg = oh; |
1241 | ||
1242 | ds_put_format(string, "%s (xid=0x%"PRIx32"):", | |
1243 | ofputil_msg_type_name(type), ntohl(oh->xid)); | |
1244 | ||
7fa91113 BP |
1245 | code = ofputil_msg_type_code(type); |
1246 | switch (code) { | |
8f93e93c | 1247 | case OFPUTIL_MSG_INVALID: |
d1e2cf21 BP |
1248 | break; |
1249 | ||
1250 | case OFPUTIL_OFPT_HELLO: | |
dc4762ed BP |
1251 | ds_put_char(string, '\n'); |
1252 | ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh, | |
1253 | 0, true); | |
d1e2cf21 BP |
1254 | break; |
1255 | ||
1256 | case OFPUTIL_OFPT_ERROR: | |
1257 | ofp_print_error_msg(string, msg); | |
1258 | break; | |
1259 | ||
1260 | case OFPUTIL_OFPT_ECHO_REQUEST: | |
1261 | case OFPUTIL_OFPT_ECHO_REPLY: | |
1262 | ofp_print_echo(string, oh, verbosity); | |
1263 | break; | |
1264 | ||
1265 | case OFPUTIL_OFPT_FEATURES_REQUEST: | |
1266 | break; | |
1267 | ||
1268 | case OFPUTIL_OFPT_FEATURES_REPLY: | |
1269 | ofp_print_switch_features(string, msg); | |
1270 | break; | |
1271 | ||
1272 | case OFPUTIL_OFPT_GET_CONFIG_REQUEST: | |
1273 | break; | |
1274 | ||
1275 | case OFPUTIL_OFPT_GET_CONFIG_REPLY: | |
1276 | case OFPUTIL_OFPT_SET_CONFIG: | |
1277 | ofp_print_switch_config(string, msg); | |
1278 | break; | |
1279 | ||
1280 | case OFPUTIL_OFPT_PACKET_IN: | |
1281 | ofp_print_packet_in(string, msg, verbosity); | |
1282 | break; | |
1283 | ||
1284 | case OFPUTIL_OFPT_FLOW_REMOVED: | |
9b045a0c BP |
1285 | case OFPUTIL_NXT_FLOW_REMOVED: |
1286 | ofp_print_flow_removed(string, msg); | |
d1e2cf21 BP |
1287 | break; |
1288 | ||
1289 | case OFPUTIL_OFPT_PORT_STATUS: | |
1290 | ofp_print_port_status(string, msg); | |
1291 | break; | |
1292 | ||
1293 | case OFPUTIL_OFPT_PACKET_OUT: | |
1294 | ofp_print_packet_out(string, msg, verbosity); | |
1295 | break; | |
1296 | ||
1297 | case OFPUTIL_OFPT_FLOW_MOD: | |
7fa91113 | 1298 | ofp_print_flow_mod(string, msg, code, verbosity); |
d1e2cf21 BP |
1299 | break; |
1300 | ||
1301 | case OFPUTIL_OFPT_PORT_MOD: | |
1302 | ofp_print_port_mod(string, msg); | |
1303 | break; | |
1304 | ||
1305 | case OFPUTIL_OFPT_BARRIER_REQUEST: | |
1306 | case OFPUTIL_OFPT_BARRIER_REPLY: | |
1307 | break; | |
1308 | ||
1309 | case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REQUEST: | |
1310 | case OFPUTIL_OFPT_QUEUE_GET_CONFIG_REPLY: | |
1311 | /* XXX */ | |
1312 | break; | |
1313 | ||
1314 | case OFPUTIL_OFPST_DESC_REQUEST: | |
1315 | ofp_print_stats_request(string, oh); | |
1316 | break; | |
1317 | ||
1318 | case OFPUTIL_OFPST_FLOW_REQUEST: | |
2af0c0ef | 1319 | case OFPUTIL_NXST_FLOW_REQUEST: |
d1e2cf21 | 1320 | case OFPUTIL_OFPST_AGGREGATE_REQUEST: |
2af0c0ef | 1321 | case OFPUTIL_NXST_AGGREGATE_REQUEST: |
d1e2cf21 | 1322 | ofp_print_stats_request(string, oh); |
76c93b22 | 1323 | ofp_print_flow_stats_request(string, msg); |
d1e2cf21 BP |
1324 | break; |
1325 | ||
1326 | case OFPUTIL_OFPST_TABLE_REQUEST: | |
1327 | ofp_print_stats_request(string, oh); | |
1328 | break; | |
1329 | ||
1330 | case OFPUTIL_OFPST_PORT_REQUEST: | |
1331 | ofp_print_stats_request(string, oh); | |
63f2140a | 1332 | ofp_print_ofpst_port_request(string, msg); |
d1e2cf21 BP |
1333 | break; |
1334 | ||
1335 | case OFPUTIL_OFPST_QUEUE_REQUEST: | |
1336 | ofp_print_stats_request(string, oh); | |
63f2140a | 1337 | ofp_print_ofpst_queue_request(string, msg); |
d1e2cf21 BP |
1338 | break; |
1339 | ||
1340 | case OFPUTIL_OFPST_DESC_REPLY: | |
1341 | ofp_print_stats_reply(string, oh); | |
63f2140a | 1342 | ofp_print_ofpst_desc_reply(string, msg); |
d1e2cf21 BP |
1343 | break; |
1344 | ||
1345 | case OFPUTIL_OFPST_FLOW_REPLY: | |
4ffd1b43 | 1346 | case OFPUTIL_NXST_FLOW_REPLY: |
d1e2cf21 | 1347 | ofp_print_stats_reply(string, oh); |
4ffd1b43 | 1348 | ofp_print_flow_stats_reply(string, oh); |
d1e2cf21 BP |
1349 | break; |
1350 | ||
1351 | case OFPUTIL_OFPST_QUEUE_REPLY: | |
1352 | ofp_print_stats_reply(string, oh); | |
1353 | ofp_print_ofpst_queue_reply(string, oh, verbosity); | |
1354 | break; | |
1355 | ||
1356 | case OFPUTIL_OFPST_PORT_REPLY: | |
1357 | ofp_print_stats_reply(string, oh); | |
1358 | ofp_print_ofpst_port_reply(string, oh, verbosity); | |
1359 | break; | |
1360 | ||
1361 | case OFPUTIL_OFPST_TABLE_REPLY: | |
1362 | ofp_print_stats_reply(string, oh); | |
1363 | ofp_print_ofpst_table_reply(string, oh, verbosity); | |
1364 | break; | |
1365 | ||
1366 | case OFPUTIL_OFPST_AGGREGATE_REPLY: | |
1367 | ofp_print_stats_reply(string, oh); | |
63f2140a | 1368 | ofp_print_ofpst_aggregate_reply(string, msg); |
d1e2cf21 | 1369 | break; |
064af421 | 1370 | |
d1e2cf21 BP |
1371 | case OFPUTIL_NXT_ROLE_REQUEST: |
1372 | case OFPUTIL_NXT_ROLE_REPLY: | |
61fe3a7b | 1373 | ofp_print_nxt_role_message(string, msg); |
7fa91113 BP |
1374 | break; |
1375 | ||
6c1491fb BP |
1376 | case OFPUTIL_NXT_FLOW_MOD_TABLE_ID: |
1377 | ofp_print_nxt_flow_mod_table_id(string, msg); | |
1378 | break; | |
1379 | ||
d1e2cf21 | 1380 | case OFPUTIL_NXT_SET_FLOW_FORMAT: |
7fa91113 BP |
1381 | ofp_print_nxt_set_flow_format(string, msg); |
1382 | break; | |
1383 | ||
d1e2cf21 | 1384 | case OFPUTIL_NXT_FLOW_MOD: |
7fa91113 BP |
1385 | ofp_print_flow_mod(string, msg, code, verbosity); |
1386 | break; | |
1387 | ||
d1e2cf21 | 1388 | case OFPUTIL_NXST_AGGREGATE_REPLY: |
a2ad9ecd | 1389 | ofp_print_stats_reply(string, oh); |
9b045a0c | 1390 | ofp_print_nxst_aggregate_reply(string, msg); |
d1e2cf21 | 1391 | break; |
246e61ea | 1392 | } |
d1e2cf21 | 1393 | } |
064af421 BP |
1394 | |
1395 | /* Composes and returns a string representing the OpenFlow packet of 'len' | |
1396 | * bytes at 'oh' at the given 'verbosity' level. 0 is a minimal amount of | |
1397 | * verbosity and higher numbers increase verbosity. The caller is responsible | |
1398 | * for freeing the string. */ | |
1399 | char * | |
1400 | ofp_to_string(const void *oh_, size_t len, int verbosity) | |
1401 | { | |
1402 | struct ds string = DS_EMPTY_INITIALIZER; | |
1403 | const struct ofp_header *oh = oh_; | |
064af421 | 1404 | |
49ad0403 BP |
1405 | if (!len) { |
1406 | ds_put_cstr(&string, "OpenFlow message is empty\n"); | |
1407 | } else if (len < sizeof(struct ofp_header)) { | |
1408 | ds_put_format(&string, "OpenFlow packet too short (only %zu bytes):\n", | |
1409 | len); | |
064af421 | 1410 | } else if (oh->version != OFP_VERSION) { |
d1e2cf21 BP |
1411 | ds_put_format(&string, "Bad OpenFlow version %"PRIu8":\n", |
1412 | oh->version); | |
1413 | } else if (ntohs(oh->length) > len) { | |
1414 | ds_put_format(&string, | |
49ad0403 | 1415 | "(***truncated to %zu bytes from %"PRIu16"***)\n", |
d1e2cf21 BP |
1416 | len, ntohs(oh->length)); |
1417 | } else if (ntohs(oh->length) < len) { | |
1418 | ds_put_format(&string, | |
1419 | "(***only uses %"PRIu16" bytes out of %zu***)\n", | |
1420 | ntohs(oh->length), len); | |
1421 | } else { | |
1422 | const struct ofputil_msg_type *type; | |
d1e2cf21 BP |
1423 | int error; |
1424 | ||
1425 | error = ofputil_decode_msg_type(oh, &type); | |
1426 | if (!error) { | |
1427 | ofp_to_string__(oh, type, &string, verbosity); | |
7fa91113 BP |
1428 | if (verbosity >= 5) { |
1429 | if (ds_last(&string) != '\n') { | |
1430 | ds_put_char(&string, '\n'); | |
1431 | } | |
d1e2cf21 BP |
1432 | ds_put_hex_dump(&string, oh, len, 0, true); |
1433 | } | |
7fa91113 | 1434 | if (ds_last(&string) != '\n') { |
d1e2cf21 BP |
1435 | ds_put_char(&string, '\n'); |
1436 | } | |
1437 | return ds_steal_cstr(&string); | |
064af421 | 1438 | } |
064af421 | 1439 | |
7fa91113 | 1440 | ofp_print_error(&string, error); |
064af421 | 1441 | } |
d1e2cf21 BP |
1442 | ds_put_hex_dump(&string, oh, len, 0, true); |
1443 | return ds_steal_cstr(&string); | |
064af421 BP |
1444 | } |
1445 | ||
1446 | /* Returns the name for the specified OpenFlow message type as a string, | |
1447 | * e.g. "OFPT_FEATURES_REPLY". If no name is known, the string returned is a | |
1448 | * hex number, e.g. "0x55". | |
1449 | * | |
1450 | * The caller must free the returned string when it is no longer needed. */ | |
1451 | char * | |
1452 | ofp_message_type_to_string(uint8_t type) | |
1453 | { | |
d1e2cf21 | 1454 | const char *name; |
064af421 | 1455 | |
d1e2cf21 BP |
1456 | switch (type) { |
1457 | case OFPT_HELLO: | |
1458 | name = "HELLO"; | |
1459 | break; | |
1460 | case OFPT_ERROR: | |
1461 | name = "ERROR"; | |
1462 | break; | |
1463 | case OFPT_ECHO_REQUEST: | |
1464 | name = "ECHO_REQUEST"; | |
1465 | break; | |
1466 | case OFPT_ECHO_REPLY: | |
1467 | name = "ECHO_REPLY"; | |
1468 | break; | |
1469 | case OFPT_VENDOR: | |
1470 | name = "VENDOR"; | |
1471 | break; | |
1472 | case OFPT_FEATURES_REQUEST: | |
1473 | name = "FEATURES_REQUEST"; | |
1474 | break; | |
1475 | case OFPT_FEATURES_REPLY: | |
1476 | name = "FEATURES_REPLY"; | |
1477 | break; | |
1478 | case OFPT_GET_CONFIG_REQUEST: | |
1479 | name = "GET_CONFIG_REQUEST"; | |
1480 | break; | |
1481 | case OFPT_GET_CONFIG_REPLY: | |
1482 | name = "GET_CONFIG_REPLY"; | |
1483 | break; | |
1484 | case OFPT_SET_CONFIG: | |
1485 | name = "SET_CONFIG"; | |
1486 | break; | |
1487 | case OFPT_PACKET_IN: | |
1488 | name = "PACKET_IN"; | |
1489 | break; | |
1490 | case OFPT_FLOW_REMOVED: | |
1491 | name = "FLOW_REMOVED"; | |
1492 | break; | |
1493 | case OFPT_PORT_STATUS: | |
1494 | name = "PORT_STATUS"; | |
1495 | break; | |
1496 | case OFPT_PACKET_OUT: | |
1497 | name = "PACKET_OUT"; | |
1498 | break; | |
1499 | case OFPT_FLOW_MOD: | |
1500 | name = "FLOW_MOD"; | |
1501 | break; | |
1502 | case OFPT_PORT_MOD: | |
1503 | name = "PORT_MOD"; | |
1504 | break; | |
1505 | case OFPT_STATS_REQUEST: | |
1506 | name = "STATS_REQUEST"; | |
1507 | break; | |
1508 | case OFPT_STATS_REPLY: | |
1509 | name = "STATS_REPLY"; | |
1510 | break; | |
1511 | case OFPT_BARRIER_REQUEST: | |
1512 | name = "BARRIER_REQUEST"; | |
1513 | break; | |
1514 | case OFPT_BARRIER_REPLY: | |
1515 | name = "BARRIER_REPLY"; | |
1516 | break; | |
1517 | case OFPT_QUEUE_GET_CONFIG_REQUEST: | |
1518 | name = "QUEUE_GET_CONFIG_REQUEST"; | |
1519 | break; | |
1520 | case OFPT_QUEUE_GET_CONFIG_REPLY: | |
1521 | name = "QUEUE_GET_CONFIG_REPLY"; | |
1522 | break; | |
1523 | default: | |
1524 | name = NULL; | |
1525 | break; | |
064af421 | 1526 | } |
d1e2cf21 BP |
1527 | |
1528 | return name ? xasprintf("OFPT_%s", name) : xasprintf("0x%02"PRIx8, type); | |
064af421 BP |
1529 | } |
1530 | \f | |
1531 | static void | |
d295e8e9 | 1532 | print_and_free(FILE *stream, char *string) |
064af421 BP |
1533 | { |
1534 | fputs(string, stream); | |
1535 | free(string); | |
1536 | } | |
1537 | ||
1538 | /* Pretty-print the OpenFlow packet of 'len' bytes at 'oh' to 'stream' at the | |
1539 | * given 'verbosity' level. 0 is a minimal amount of verbosity and higher | |
1540 | * numbers increase verbosity. */ | |
1541 | void | |
1542 | ofp_print(FILE *stream, const void *oh, size_t len, int verbosity) | |
1543 | { | |
1544 | print_and_free(stream, ofp_to_string(oh, len, verbosity)); | |
1545 | } | |
1546 | ||
1547 | /* Dumps the contents of the Ethernet frame in the 'len' bytes starting at | |
1548 | * 'data' to 'stream' using tcpdump. 'total_len' specifies the full length of | |
1549 | * the Ethernet frame (of which 'len' bytes were captured). | |
1550 | * | |
1551 | * This starts and kills a tcpdump subprocess so it's quite expensive. */ | |
1552 | void | |
1553 | ofp_print_packet(FILE *stream, const void *data, size_t len, size_t total_len) | |
1554 | { | |
1555 | print_and_free(stream, ofp_packet_to_string(data, len, total_len)); | |
1556 | } |