/* * arpra_parse_packet * * Parse ARP packet and extract info */ static indigo_error_t arpra_parse_packet (ppe_packet_t *ppep, arp_info_t *info, bool vlan_tagged) { uint32_t tmp; if (!ppep || !info) return INDIGO_ERROR_PARAM; ppe_wide_field_get(ppep, PPE_FIELD_ETHERNET_DST_MAC, info->eth_dst.addr); ppe_wide_field_get(ppep, PPE_FIELD_ETHERNET_SRC_MAC, info->eth_src.addr); if (vlan_tagged) { ppe_field_get(ppep, PPE_FIELD_8021Q_VLAN, &tmp); info->vlan_vid = tmp; ppe_field_get(ppep, PPE_FIELD_8021Q_PRI, &tmp); info->vlan_pcp = tmp; } ppe_field_get(ppep, PPE_FIELD_ARP_HTYPE, &tmp); if (tmp != 1) { return INDIGO_ERROR_PARSE; } ppe_field_get(ppep, PPE_FIELD_ARP_PTYPE, &tmp); if (tmp != 0x0800) { return INDIGO_ERROR_PARSE; } ppe_field_get(ppep, PPE_FIELD_ARP_HLEN, &tmp); if (tmp != 6) { return INDIGO_ERROR_PARSE; } ppe_field_get(ppep, PPE_FIELD_ARP_PLEN, &tmp); if (tmp != 4) { return INDIGO_ERROR_PARSE; } ppe_field_get(ppep, PPE_FIELD_ARP_OPERATION, &tmp); info->operation = tmp; ppe_wide_field_get(ppep, PPE_FIELD_ARP_SHA, info->sender.mac.addr); ppe_field_get(ppep, PPE_FIELD_ARP_SPA, &tmp); info->sender.ipv4 = tmp; ppe_wide_field_get(ppep, PPE_FIELD_ARP_THA, info->target.mac.addr); ppe_field_get(ppep, PPE_FIELD_ARP_TPA, &tmp); info->target.ipv4 = tmp; return INDIGO_ERROR_NONE; }
void icmpa_verify_packet (of_octets_t *octets, uint32_t reason) { ppe_packet_t ppep; uint32_t icmp_type; if (!octets) return; ppe_packet_init(&ppep, octets->data, octets->bytes); if (ppe_parse(&ppep) < 0) { AIM_LOG_ERROR("Packet_in parsing failed."); return; } assert(ppe_header_get(&ppep, PPE_HEADER_ICMP)); ppe_field_get(&ppep, PPE_FIELD_ICMP_TYPE, &icmp_type); assert(icmp_type == reason); }
static ucli_status_t ppe_ucli_utm__fdump__(ucli_context_t* uc) { ppe_field_t field; ppe_header_t format; UCLI_COMMAND_INFO(uc, "fdump", 0, "Dump all packet fields."); ppe_packet_format_get(&ppec->ppep, &format); ucli_printf(uc, "format=%{ppe_header}, size=%d\n", format, ppec->ppep.size); for(field = 0; field < PPE_FIELD_COUNT; field++) { const ppe_field_info_t* fi = ppe_field_info_get(field); if(fi->size_bits == 0) { continue; } if(ppe_field_exists(&ppec->ppep, field) == 0) { continue; } if(fi->size_bits <= 32) { uint32_t data; ppe_field_get(&ppec->ppep, field, &data); ucli_printf(uc, "%{ppe_field} = 0x%x (%d)\n", field, data, data); } else { uint8_t* p = ppe_fieldp_get(&ppec->ppep, field); ucli_printf(uc, "%{ppe_field} = %{data}\n", field, p, fi->size_bits/8); } } return UCLI_STATUS_OK; }
static ucli_status_t ppe_ucli_utm__rwall__(ucli_context_t* uc) { ppe_packet_t ppep; ppe_header_t header; ppe_field_t f; int rv = UCLI_STATUS_OK; UCLI_COMMAND_INFO(uc, "rwall", 0, "Read and write all packet fields in all headers."); ppe_packet_init(&ppep, NULL, 0); /** * Allocate and assign a header pointer for every header type. * All bits will be initialized to 1. */ for(header = 0; header < PPE_HEADER_COUNT; header++) { uint8_t* hp = aim_zmalloc(1000); PPE_MEMSET(hp, 0xFF, 1000); ppe_header_set(&ppep, header, hp); } /** * Check that every field reads back as all 1's, with the correct width */ for(f = 0; f < PPE_FIELD_COUNT; f++) { const ppe_field_info_t* fi = ppe_field_info_get(f); if(fi->size_bits == 0) { continue; } if(fi->size_bits <= 32) { uint32_t v; ppe_field_get(&ppep, f, &v); if(fi->size_bits == 32) { if(v != 0xFFFFFFFF) { rv = ucli_error(uc, "first read: field %{ppe_field} is 0x%x, should be 0x%x", f, v, -1); } } else { if(v != ( (1U << fi->size_bits) - 1)) { rv = ucli_error(uc, "first read: field %{ppe_field} is 0x%x, should be 0x%x (%d bits)", f, v, (1<<fi->size_bits) - 1, fi->size_bits); } } /** clear field and re-read */ ppe_field_set(&ppep, f, 0); ppe_field_get(&ppep, f, &v); if(v != 0) { rv = ucli_error(uc, "second read: field %{ppe_field} is 0x%x when it should be 0.", f, v); } } else { uint8_t vb[64]; int bytes = ppe_wide_field_get(&ppep, f, vb); int i; for(i = 0; i < bytes; i++) { if(vb[i] != 0xFF) { rv = ucli_error(uc, "first read: field %{ppe_field}[%d] is 0x%.2x, should be 0x%.2x", f, i, vb[i], 0xFF); } } PPE_MEMSET(vb, 0, sizeof(vb)); /** clear field and re-read */ ppe_wide_field_set(&ppep, f, vb); PPE_MEMSET(vb, 0xFF, sizeof(vb)); ppe_wide_field_get(&ppep, f, vb); for(i = 0; i < bytes; i++) { if(vb[i] != 0) { rv = ucli_error(uc, "second read: field %{ppe_field}[%d] is 0x%.2x, should be 0.", f, i, vb[i]); } } } /** continue reading other fields, making sure the field we just cleared * does not change the value of fields we have not yet visited. */ } for(header = 0; header < PPE_HEADER_COUNT; header++) { aim_free(ppe_header_get(&ppep, header)); } return rv; }
static ucli_status_t ppe_ucli_utm__dfk__(ucli_context_t* uc) { ppe_dfk_t dfk; ppe_field_t fields[4]; uint8_t* verify_data; unsigned int verify_data_size; int rv = UCLI_STATUS_OK; unsigned int i; UCLI_COMMAND_INFO(uc, "dfk", AIM_ARRAYSIZE(fields)+1, "Generate and verify a dynamic field key."); UCLI_ARGPARSE_OR_RETURN(uc, "{ppe_field}{ppe_field}{ppe_field}{ppe_field}{data}", fields+0, fields+1, fields+2, fields+3, &verify_data, &verify_data_size); ppe_dfk_init(&dfk, fields, AIM_ARRAYSIZE(fields)); i = ppe_packet_dfk(&ppec->ppep, &dfk); if(i != verify_data_size) { rv = ucli_error(uc, "dfk size is %d, verify data size is %d", i, verify_data_size); goto dfk_error; } for(i = 0; i < AIM_ARRAYSIZE(fields); i++) { const ppe_field_info_t* fi = ppe_field_info_get(fields[i]); int exists = ppe_field_exists(&ppec->ppep, fi->field); if(exists && ( (dfk.mask & (1<<i)) == 0)) { /* Should be in the field key but isn't.*/ rv = ucli_error(uc, "%{ppe_field} exists in packet but not in field key.", fi->field); goto dfk_error; } if(!(exists) && (dfk.mask & (1<<i))) { /* Should not be in the field key but is. */ rv = ucli_error(uc, "%{ppe_field} is in the key but not the packet.", fi->field); goto dfk_error; } } for(i = 0; i < verify_data_size; i++) { if(verify_data[i] != dfk.data[i]) { rv = ucli_error(uc, "key data mismatch at byte %d.\nkey=%{data}, verify=%{data}", i, dfk.data, verify_data_size, verify_data, verify_data_size); goto dfk_error; } } for(i = 0; i < AIM_ARRAYSIZE(fields); i++) { if(dfk.mask & (1<<i)) { const ppe_field_info_t* fi = ppe_field_info_get(fields[i]); if(fi->size_bits <= 32) { uint32_t pdata; uint32_t kdata; ppe_field_get(&ppec->ppep, fi->field, &pdata); ppe_dfk_field_get(&dfk, fi->field, &kdata); if(pdata != kdata) { rv = ucli_error(uc, "field_get mismatch: p=0x%x, k=0x%x"); goto dfk_error; } } else { unsigned int i; uint8_t pdata[128]; uint8_t kdata[128]; ppe_wide_field_get(&ppec->ppep, fi->field, pdata); ppe_dfk_wide_field_get(&dfk, fi->field, kdata); for(i = 0; i < fi->size_bits/8; i++) { if(pdata[i] != kdata[i]) { rv = ucli_error(uc, "wide_field_get mismatch @ %d: p=0x%x k=0x%x", i, pdata[i], kdata[i]); goto dfk_error; } } } } } aim_free(verify_data); ppe_dfk_destroy(&dfk); return UCLI_STATUS_OK; dfk_error: ucli_printf(uc, "key: "); ppe_dfk_show(&dfk, &uc->pvs); ppe_dfk_destroy(&dfk); aim_free(verify_data); ucli_printf(uc, "\n"); return rv; }
/* * icmp_packet_in_handler * * API for handling incoming packets */ indigo_core_listener_result_t icmpa_packet_in_handler (of_packet_in_t *packet_in) { of_octets_t octets; of_port_no_t port_no; of_match_t match; ppe_packet_t ppep; indigo_core_listener_result_t result = INDIGO_CORE_LISTENER_RESULT_PASS; uint32_t type, code; debug_counter_inc(&pkt_counters.icmp_total_in_packets); if (!packet_in) return INDIGO_CORE_LISTENER_RESULT_PASS; of_packet_in_data_get(packet_in, &octets); /* * Identify the recv port */ if (packet_in->version <= OF_VERSION_1_1) { return INDIGO_CORE_LISTENER_RESULT_PASS; } else { if (of_packet_in_match_get(packet_in, &match) < 0) { AIM_LOG_ERROR("ICMPA: match get failed"); debug_counter_inc(&pkt_counters.icmp_internal_errors); return INDIGO_CORE_LISTENER_RESULT_PASS; } port_no = match.fields.in_port; } if (port_no == OF_PORT_DEST_CONTROLLER) { debug_counter_inc(&pkt_counters.icmp_total_passed_packets); return INDIGO_CORE_LISTENER_RESULT_PASS; } if (port_no > MAX_PORTS) { AIM_LOG_ERROR("ICMPA: Port No: %d Out of Range %d", port_no, MAX_PORTS); debug_counter_inc(&pkt_counters.icmp_internal_errors); return INDIGO_CORE_LISTENER_RESULT_PASS; } /* * Check the packet-in reasons in metadata * * Icmp agent should not consume packets coming in due to L2 Src miss * and Station Move. */ if ((match.fields.metadata & OFP_BSN_PKTIN_FLAG_STATION_MOVE) || (match.fields.metadata & OFP_BSN_PKTIN_FLAG_NEW_HOST)) { debug_counter_inc(&pkt_counters.icmp_total_passed_packets); return INDIGO_CORE_LISTENER_RESULT_PASS; } ppe_packet_init(&ppep, octets.data, octets.bytes); if (ppe_parse(&ppep) < 0) { AIM_LOG_RL_ERROR(&icmp_pktin_log_limiter, os_time_monotonic(), "ICMPA: Packet_in parsing failed."); debug_counter_inc(&pkt_counters.icmp_internal_errors); return INDIGO_CORE_LISTENER_RESULT_PASS; } /* * Identify if this is an Echo Request, destined to one of VRouter */ if (ppe_header_get(&ppep, PPE_HEADER_ICMP)) { if (icmpa_reply(&ppep, port_no, &result)) { ++port_pkt_counters[port_no].icmp_echo_packets; return result; } } /* * To handle traceroute, we need to check for * a) UDP Packet * b) dest IP is Vrouter IP * c) UDP src and dest ports are ephemeral */ if (ppe_header_get(&ppep, PPE_HEADER_UDP) && ppe_header_get(&ppep, PPE_HEADER_IP4)) { uint32_t dest_ip, src_port, dest_port; ppe_field_get(&ppep, PPE_FIELD_IP4_DST_ADDR, &dest_ip); ppe_field_get(&ppep, PPE_FIELD_UDP_SRC_PORT, &src_port); ppe_field_get(&ppep, PPE_FIELD_UDP_DST_PORT, &dest_port); if (router_ip_check(dest_ip) && is_ephemeral(src_port) && is_ephemeral(dest_port)) { AIM_LOG_TRACE("ICMP Port Unreachable received on port: %d", port_no); type = ICMP_DEST_UNREACHABLE; code = 3; result = INDIGO_CORE_LISTENER_RESULT_DROP; if (icmpa_send(&ppep, port_no, type, code)) { ++port_pkt_counters[port_no].icmp_port_unreachable_packets; return result; } } } /* * Identify if the reason is valid for ICMP Agent to consume the packet */ if (match.fields.metadata & OFP_BSN_PKTIN_FLAG_L3_MISS) { AIM_LOG_TRACE("ICMP Dest Network Unreachable received on port: %d", port_no); type = ICMP_DEST_UNREACHABLE; code = 0; result = INDIGO_CORE_LISTENER_RESULT_DROP; if (icmpa_send(&ppep, port_no, type, code)) { ++port_pkt_counters[port_no].icmp_net_unreachable_packets; } } else if (match.fields.metadata & OFP_BSN_PKTIN_FLAG_TTL_EXPIRED) { AIM_LOG_TRACE("ICMP TTL Expired received on port: %d", port_no); type = ICMP_TIME_EXCEEDED; code = 0; result = INDIGO_CORE_LISTENER_RESULT_DROP; if (icmpa_send(&ppep, port_no, type, code)) { ++port_pkt_counters[port_no].icmp_time_exceeded_packets; } } return result; }