/* Tries to decode 'oh', which should be an OpenFlow OFPT_ERROR message. * Returns an OFPERR_* constant on success, 0 on failure. * * If 'payload' is nonnull, on success '*payload' is initialized with a copy of * the error's payload (copying is required because the payload is not properly * aligned). The caller must free the payload (with ofpbuf_uninit()) when it * is no longer needed. On failure, '*payload' is cleared. */ enum ofperr ofperr_decode_msg(const struct ofp_header *oh, struct ofpbuf *payload) { const struct ofp_error_msg *oem; enum ofpraw raw; uint16_t type, code; enum ofperr error; uint32_t vendor; struct ofpbuf b; if (payload) { memset(payload, 0, sizeof *payload); } /* Pull off the error message. */ ofpbuf_use_const(&b, oh, ntohs(oh->length)); error = ofpraw_pull(&raw, &b); if (error) { return 0; } oem = ofpbuf_pull(&b, sizeof *oem); /* Get the error type and code. */ vendor = 0; type = ntohs(oem->type); code = ntohs(oem->code); if (type == NXET_VENDOR && code == NXVC_VENDOR_ERROR) { const struct nx_vendor_error *nve = ofpbuf_try_pull(&b, sizeof *nve); if (!nve) { return 0; } vendor = ntohl(nve->vendor); type = ntohs(nve->type); code = ntohs(nve->code); } else if (type == OFPET12_EXPERIMENTER) { const ovs_be32 *vendorp = ofpbuf_try_pull(&b, sizeof *vendorp); if (!vendorp) { return 0; } vendor = ntohl(*vendorp); type = code; code = 0; } /* Translate the error type and code into an ofperr. */ error = ofperr_decode(oh->version, vendor, type, code); if (error && payload) { ofpbuf_init(payload, b.size); ofpbuf_push(payload, b.data, b.size); } return error; }
/* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract * ofputil_queue_stats in 'qs'. * * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow * message. Calling this function multiple times for a single 'msg' iterates * through the replies. The caller must initially leave 'msg''s layer pointers * null and not modify them between calls. * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) { enum ofperr error; enum ofpraw raw; error = (msg->header ? ofpraw_decode(&raw, msg->header) : ofpraw_pull(&raw, msg)); if (error) { return error; } if (!msg->size) { return EOF; } else if (raw == OFPRAW_OFPST14_QUEUE_REPLY) { return ofputil_pull_ofp14_queue_stats(qs, msg); } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) { const struct ofp13_queue_stats *qs13; qs13 = ofpbuf_try_pull(msg, sizeof *qs13); if (!qs13) { goto bad_len; } return ofputil_queue_stats_from_ofp13(qs, qs13); } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) { const struct ofp11_queue_stats *qs11; qs11 = ofpbuf_try_pull(msg, sizeof *qs11); if (!qs11) { goto bad_len; } return ofputil_queue_stats_from_ofp11(qs, qs11); } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) { const struct ofp10_queue_stats *qs10; qs10 = ofpbuf_try_pull(msg, sizeof *qs10); if (!qs10) { goto bad_len; } return ofputil_queue_stats_from_ofp10(qs, qs10); } else { OVS_NOT_REACHED(); } bad_len: VLOG_WARN_RL(&rl, "OFPST_QUEUE reply has %"PRIu32" leftover " "bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; }
void handle_acl_log(const struct flow *headers, struct ofpbuf *userdata) { if (!VLOG_IS_INFO_ENABLED()) { return; } struct log_pin_header *lph = ofpbuf_try_pull(userdata, sizeof *lph); if (!lph) { VLOG_WARN("log data missing"); return; } size_t name_len = userdata->size; char *name = name_len ? xmemdup0(userdata->data, name_len) : NULL; struct ds ds = DS_EMPTY_INITIALIZER; ds_put_cstr(&ds, "name="); json_string_escape(name_len ? name : "<unnamed>", &ds); ds_put_format(&ds, ", verdict=%s, severity=%s: ", log_verdict_to_string(lph->verdict), log_severity_to_string(lph->severity)); flow_format(&ds, headers, NULL); VLOG_INFO("%s", ds_cstr(&ds)); ds_destroy(&ds); free(name); }
static enum ofperr oxm_pull_match__(struct ofpbuf *b, bool strict, struct match *match) { struct ofp11_match_header *omh = ofpbuf_data(b); uint8_t *p; uint16_t match_len; if (ofpbuf_size(b) < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } match_len = ntohs(omh->length); if (match_len < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } if (omh->type != htons(OFPMT_OXM)) { return OFPERR_OFPBMC_BAD_TYPE; } p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); if (!p) { VLOG_DBG_RL(&rl, "oxm length %u, rounded up to a " "multiple of 8, is longer than space in message (max " "length %"PRIu32")", match_len, ofpbuf_size(b)); return OFPERR_OFPBMC_BAD_LEN; } return nx_pull_raw(p + sizeof *omh, match_len - sizeof *omh, strict, match, NULL, NULL); }
static int ofputil_pull_queue_get_config_reply14(struct ofpbuf *msg, struct ofputil_queue_config *queue) { struct ofp14_queue_desc *oqd14 = ofpbuf_try_pull(msg, sizeof *oqd14); if (!oqd14) { return OFPERR_OFPBRC_BAD_LEN; } enum ofperr error = ofputil_port_from_ofp11(oqd14->port_no, &queue->port); if (error) { return error; } queue->queue = ntohl(oqd14->queue_id); /* Length check. */ unsigned int len = ntohs(oqd14->len); if (len < sizeof *oqd14 || len > msg->size + sizeof *oqd14 || len % 8) { return OFPERR_OFPBRC_BAD_LEN; } len -= sizeof *oqd14; struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len), len); while (properties.size > 0) { struct ofpbuf payload; uint64_t type; error = ofpprop_pull(&properties, &payload, &type); if (error) { return error; } switch (type) { case OFPQDPT14_MIN_RATE: error = ofpprop_parse_u16(&payload, &queue->min_rate); break; case OFPQDPT14_MAX_RATE: error = ofpprop_parse_u16(&payload, &queue->max_rate); break; default: error = OFPPROP_UNKNOWN(true, "queue desc", type); break; } if (error) { return error; } } return 0; }
static void parse_mpls(struct ofpbuf *b, struct flow *flow) { struct mpls_hdr *mh; while ((mh = ofpbuf_try_pull(b, sizeof *mh))) { if (flow->mpls_depth++ == 0) { flow->mpls_lse = mh->mpls_lse; } if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { break; } } }
static void parse_mpls(struct ofpbuf *b, struct flow *flow) { struct mpls_hdr *mh; bool top = true; while ((mh = ofpbuf_try_pull(b, sizeof *mh))) { if (top) { top = false; flow->mpls_lse = mh->mpls_lse; } if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) { break; } } }
static enum ofperr nx_pull_match__(struct ofpbuf *b, unsigned int match_len, bool strict, struct match *match, ovs_be64 *cookie, ovs_be64 *cookie_mask) { uint8_t *p = NULL; if (match_len) { p = ofpbuf_try_pull(b, ROUND_UP(match_len, 8)); if (!p) { VLOG_DBG_RL(&rl, "nx_match length %u, rounded up to a " "multiple of 8, is longer than space in message (max " "length %"PRIu32")", match_len, ofpbuf_size(b)); return OFPERR_OFPBMC_BAD_LEN; } } return nx_pull_raw(p, match_len, strict, match, cookie, cookie_mask); }
static enum ofperr ofputil_pull_ofp14_queue_stats(struct ofputil_queue_stats *oqs, struct ofpbuf *msg) { const struct ofp14_queue_stats *qs14; size_t len; qs14 = ofpbuf_try_pull(msg, sizeof *qs14); if (!qs14) { return OFPERR_OFPBRC_BAD_LEN; } len = ntohs(qs14->length); if (len < sizeof *qs14 || len - sizeof *qs14 > msg->size) { return OFPERR_OFPBRC_BAD_LEN; } ofpbuf_pull(msg, len - sizeof *qs14); /* No properties yet defined, so ignore them for now. */ return ofputil_queue_stats_from_ofp13(oqs, &qs14->qs); }
static int parse_ipv6(struct ofpbuf *packet, struct flow *flow) { const struct ovs_16aligned_ip6_hdr *nh; ovs_be32 tc_flow; int nexthdr; nh = ofpbuf_try_pull(packet, sizeof *nh); if (!nh) { return EINVAL; } nexthdr = nh->ip6_nxt; memcpy(&flow->ipv6_src, &nh->ip6_src, sizeof flow->ipv6_src); memcpy(&flow->ipv6_dst, &nh->ip6_dst, sizeof flow->ipv6_dst); tc_flow = get_16aligned_be32(&nh->ip6_flow); flow->nw_tos = ntohl(tc_flow) >> 20; flow->ipv6_label = tc_flow & htonl(IPV6_LABEL_MASK); flow->nw_ttl = nh->ip6_hlim; flow->nw_proto = IPPROTO_NONE; while (1) { if ((nexthdr != IPPROTO_HOPOPTS) && (nexthdr != IPPROTO_ROUTING) && (nexthdr != IPPROTO_DSTOPTS) && (nexthdr != IPPROTO_AH) && (nexthdr != IPPROTO_FRAGMENT)) { /* It's either a terminal header (e.g., TCP, UDP) or one we * don't understand. In either case, we're done with the * packet, so use it to fill in 'nw_proto'. */ break; } /* We only verify that at least 8 bytes of the next header are * available, but many of these headers are longer. Ensure that * accesses within the extension header are within those first 8 * bytes. All extension headers are required to be at least 8 * bytes. */ if (packet->size < 8) { return EINVAL; } if ((nexthdr == IPPROTO_HOPOPTS) || (nexthdr == IPPROTO_ROUTING) || (nexthdr == IPPROTO_DSTOPTS)) { /* These headers, while different, have the fields we care about * in the same location and with the same interpretation. */ const struct ip6_ext *ext_hdr = packet->data; nexthdr = ext_hdr->ip6e_nxt; if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 1) * 8)) { return EINVAL; } } else if (nexthdr == IPPROTO_AH) { /* A standard AH definition isn't available, but the fields * we care about are in the same location as the generic * option header--only the header length is calculated * differently. */ const struct ip6_ext *ext_hdr = packet->data; nexthdr = ext_hdr->ip6e_nxt; if (!ofpbuf_try_pull(packet, (ext_hdr->ip6e_len + 2) * 4)) { return EINVAL; } } else if (nexthdr == IPPROTO_FRAGMENT) { const struct ovs_16aligned_ip6_frag *frag_hdr = packet->data; nexthdr = frag_hdr->ip6f_nxt; if (!ofpbuf_try_pull(packet, sizeof *frag_hdr)) { return EINVAL; } /* We only process the first fragment. */ if (frag_hdr->ip6f_offlg != htons(0)) { flow->nw_frag = FLOW_NW_FRAG_ANY; if ((frag_hdr->ip6f_offlg & IP6F_OFF_MASK) != htons(0)) { flow->nw_frag |= FLOW_NW_FRAG_LATER; nexthdr = IPPROTO_FRAGMENT; break; } } } } flow->nw_proto = nexthdr; return 0; }
static void print_netflow(struct ofpbuf *buf) { const struct netflow_v5_header *hdr; int i; hdr = ofpbuf_try_pull(buf, sizeof *hdr); if (!hdr) { printf("truncated NetFlow packet header\n"); return; } printf("header: v%"PRIu16", " "uptime %"PRIu32", " "now %"PRIu32".%09"PRIu32", " "seq %"PRIu32", " "engine %"PRIu8",%"PRIu8, ntohs(hdr->version), ntohl(hdr->sysuptime), ntohl(hdr->unix_secs), ntohl(hdr->unix_nsecs), ntohl(hdr->flow_seq), hdr->engine_type, hdr->engine_id); if (hdr->sampling_interval != htons(0)) { printf(", interval %"PRIu16, ntohs(hdr->sampling_interval)); } putchar('\n'); for (i = 0; i < ntohs(hdr->count); i++) { struct netflow_v5_record *rec; rec = ofpbuf_try_pull(buf, sizeof *rec); if (!rec) { printf("truncated NetFlow records\n"); return; } printf("seq %"PRIu32": "IP_FMT" > "IP_FMT, ntohl(hdr->flow_seq), IP_ARGS(rec->src_addr), IP_ARGS(rec->dst_addr)); printf(", if %"PRIu16" > %"PRIu16, ntohs(rec->input), ntohs(rec->output)); printf(", %"PRIu32" pkts, %"PRIu32" bytes", ntohl(rec->packet_count), ntohl(rec->byte_count)); switch (rec->ip_proto) { case IPPROTO_TCP: printf(", TCP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); if (rec->tcp_flags) { struct ds s = DS_EMPTY_INITIALIZER; packet_format_tcp_flags(&s, rec->tcp_flags); printf(" %s", ds_cstr(&s)); ds_destroy(&s); } break; case IPPROTO_UDP: printf(", UDP %"PRIu16" > %"PRIu16, ntohs(rec->src_port), ntohs(rec->dst_port)); break; case IPPROTO_ICMP: printf(", ICMP %"PRIu16":%"PRIu16, ntohs(rec->dst_port) >> 8, ntohs(rec->dst_port) & 0xff); if (rec->src_port != htons(0)) { printf(", src_port=%"PRIu16, ntohs(rec->src_port)); } break; default: printf(", proto %"PRIu8, rec->ip_proto); break; } if (rec->ip_proto != IPPROTO_TCP && rec->tcp_flags != 0) { printf(", flags %"PRIx8, rec->tcp_flags); } if (rec->ip_proto != IPPROTO_TCP && rec->ip_proto != IPPROTO_UDP && rec->ip_proto != IPPROTO_ICMP) { if (rec->src_port != htons(0)) { printf(", src_port %"PRIu16, ntohs(rec->src_port)); } if (rec->dst_port != htons(0)) { printf(", dst_port %"PRIu16, ntohs(rec->dst_port)); } } if (rec->ip_tos) { printf(", TOS %"PRIx8, rec->ip_tos); } printf(", time %"PRIu32"...%"PRIu32, ntohl(rec->init_time), ntohl(rec->used_time)); if (rec->nexthop != htonl(0)) { printf(", nexthop "IP_FMT, IP_ARGS(rec->nexthop)); } if (rec->src_as != htons(0) || rec->dst_as != htons(0)) { printf(", AS %"PRIu16" > %"PRIu16, ntohs(rec->src_as), ntohs(rec->dst_as)); } if (rec->src_mask != 0 || rec->dst_mask != 0) { printf(", mask %"PRIu8" > %"PRIu8, rec->src_mask, rec->dst_mask); } if (rec->pad1) { printf(", pad1 %"PRIu8, rec->pad1); } if (rec->pad[0] || rec->pad[1]) { printf(", pad %"PRIu8", %"PRIu8, rec->pad[0], rec->pad[1]); } putchar('\n'); } if (buf->size) { printf("%zu extra bytes after last record\n", buf->size); } }
static struct icmp6_hdr * pull_icmpv6(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, sizeof(struct icmp6_hdr)); }
static struct icmp_header * pull_icmp(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, ICMP_HEADER_LEN); }
static struct sctp_header * pull_sctp(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, SCTP_HEADER_LEN); }
static struct udp_header * pull_udp(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, UDP_HEADER_LEN); }
static struct arp_eth_header * pull_arp(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, ARP_ETH_HEADER_LEN); }
static bool parse_icmpv6(struct ofpbuf *b, struct flow *flow) { const struct icmp6_hdr *icmp = pull_icmpv6(b); if (!icmp) { return false; } /* The ICMPv6 type and code fields use the 16-bit transport port * fields, so we need to store them in 16-bit network byte order. */ flow->tp_src = htons(icmp->icmp6_type); flow->tp_dst = htons(icmp->icmp6_code); if (icmp->icmp6_code == 0 && (icmp->icmp6_type == ND_NEIGHBOR_SOLICIT || icmp->icmp6_type == ND_NEIGHBOR_ADVERT)) { const struct in6_addr *nd_target; nd_target = ofpbuf_try_pull(b, sizeof *nd_target); if (!nd_target) { return false; } flow->nd_target = *nd_target; while (b->size >= 8) { /* The minimum size of an option is 8 bytes, which also is * the size of Ethernet link-layer options. */ const struct nd_opt_hdr *nd_opt = b->data; int opt_len = nd_opt->nd_opt_len * 8; if (!opt_len || opt_len > b->size) { goto invalid; } /* Store the link layer address if the appropriate option is * provided. It is considered an error if the same link * layer option is specified twice. */ if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) { if (eth_addr_is_zero(flow->arp_sha)) { memcpy(flow->arp_sha, nd_opt + 1, ETH_ADDR_LEN); } else { goto invalid; } } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR && opt_len == 8) { if (eth_addr_is_zero(flow->arp_tha)) { memcpy(flow->arp_tha, nd_opt + 1, ETH_ADDR_LEN); } else { goto invalid; } } if (!ofpbuf_try_pull(b, opt_len)) { goto invalid; } } } return true; invalid: memset(&flow->nd_target, 0, sizeof(flow->nd_target)); memset(flow->arp_sha, 0, sizeof(flow->arp_sha)); memset(flow->arp_tha, 0, sizeof(flow->arp_tha)); return false; }
static struct vlan_header * pull_vlan(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, VLAN_HEADER_LEN); }
static struct eth_header * pull_eth(struct ofpbuf *packet) { return ofpbuf_try_pull(packet, ETH_HEADER_LEN); }
netdev_windows_netdev_from_ofpbuf(struct netdev_windows_netdev_info *info, struct ofpbuf *buf) { static const struct nl_policy ovs_netdev_policy[] = { [OVS_WIN_NETDEV_ATTR_PORT_NO] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_TYPE] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_NAME] = { .type = NL_A_STRING, .max_len = IFNAMSIZ }, [OVS_WIN_NETDEV_ATTR_MAC_ADDR] = { NL_POLICY_FOR(info->mac_address) }, [OVS_WIN_NETDEV_ATTR_MTU] = { .type = NL_A_U32 }, [OVS_WIN_NETDEV_ATTR_IF_FLAGS] = { .type = NL_A_U32 }, }; netdev_windows_info_init(info); struct ofpbuf b = ofpbuf_const_initializer(buf->data, buf->size); struct nlmsghdr *nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg); struct genlmsghdr *genl = ofpbuf_try_pull(&b, sizeof *genl); struct ovs_header *ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header); struct nlattr *a[ARRAY_SIZE(ovs_netdev_policy)]; if (!nlmsg || !genl || !ovs_header || nlmsg->nlmsg_type != ovs_win_netdev_family || !nl_policy_parse(&b, 0, ovs_netdev_policy, a, ARRAY_SIZE(ovs_netdev_policy))) { return EINVAL; } info->cmd = genl->cmd; info->dp_ifindex = ovs_header->dp_ifindex; info->port_no = nl_attr_get_odp_port(a[OVS_WIN_NETDEV_ATTR_PORT_NO]); info->ovs_type = nl_attr_get_u32(a[OVS_WIN_NETDEV_ATTR_TYPE]);
static int ofputil_pull_queue_get_config_reply10(struct ofpbuf *msg, struct ofputil_queue_config *queue) { const struct ofp_header *oh = msg->header; unsigned int opq_len; /* Length of protocol-specific queue header. */ unsigned int len; /* Total length of queue + properties. */ /* Obtain the port number from the message header. */ if (oh->version == OFP10_VERSION) { const struct ofp10_queue_get_config_reply *oqgcr10 = msg->msg; queue->port = u16_to_ofp(ntohs(oqgcr10->port)); } else { const struct ofp11_queue_get_config_reply *oqgcr11 = msg->msg; enum ofperr error = ofputil_port_from_ofp11(oqgcr11->port, &queue->port); if (error) { return error; } } /* Pull off the queue header and get the queue number and length. */ if (oh->version < OFP12_VERSION) { const struct ofp10_packet_queue *opq10; opq10 = ofpbuf_try_pull(msg, sizeof *opq10); if (!opq10) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue = ntohl(opq10->queue_id); len = ntohs(opq10->len); opq_len = sizeof *opq10; } else { const struct ofp12_packet_queue *opq12; opq12 = ofpbuf_try_pull(msg, sizeof *opq12); if (!opq12) { return OFPERR_OFPBRC_BAD_LEN; } queue->queue = ntohl(opq12->queue_id); len = ntohs(opq12->len); opq_len = sizeof *opq12; } /* Length check. */ if (len < opq_len || len > msg->size + opq_len || len % 8) { return OFPERR_OFPBRC_BAD_LEN; } len -= opq_len; /* Pull properties. The format of these properties differs from used in * OF1.4+ so we can't use the common property functions. */ while (len > 0) { const struct ofp10_queue_prop_header *hdr; unsigned int property; unsigned int prop_len; enum ofperr error = 0; hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr); prop_len = ntohs(hdr->len); if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) { return OFPERR_OFPBRC_BAD_LEN; } property = ntohs(hdr->property); switch (property) { case OFPQT10_MIN_RATE: error = parse_ofp10_queue_rate(hdr, &queue->min_rate); break; case OFPQT11_MAX_RATE: error = parse_ofp10_queue_rate(hdr, &queue->max_rate); break; default: VLOG_INFO_RL(&rl, "unknown queue property %u", property); break; } if (error) { return error; } ofpbuf_pull(msg, prop_len); len -= prop_len; } return 0; }