/* Parse the given ICMP type and code strings, and fill in the * *type and *code with the results. If there is an error during * parsing, fill in *error and return STATUS_ERR; otherwise return * STATUS_OK. */ static int parse_icmp_type_and_code(int address_family, const char *type_string, const char *code_string, s32 *type, s32 *code, char **error) { int i = 0; const struct icmp_type_info *icmp_types = NULL; const struct icmp_code_info *code_table = NULL; /* for this type */ if (address_family == AF_INET) icmp_types = icmpv4_types; else if (address_family == AF_INET6) icmp_types = icmpv6_types; else assert(!"bad ip_version in config"); /* Parse the type string. */ if (sscanf(type_string, "type_%d", type) == 1) { /* Legal but non-standard type in tcpdump-inspired notation. */ } else { /* Look in our table of known types. */ for (i = 0; icmp_types[i].type_string != NULL; ++i) { if (!strcmp(type_string, icmp_types[i].type_string)) { *type = icmp_types[i].type_byte; code_table = icmp_types[i].code_table; } } } if (!is_valid_u8(*type)) { asprintf(error, "bad ICMP type %s", type_string); return STATUS_ERR; } /* Parse the code string. */ if (code_string == NULL) { *code = 0; /* missing code means code = 0 */ } else if (sscanf(code_string, "code_%d", code) == 1) { /* Legal but non-standard code in tcpdump-inspired notation. */ } else if (code_table != NULL) { /* Look in our table of known codes. */ for (i = 0; code_table[i].code_string != NULL; ++i) { if (!strcmp(code_string, code_table[i].code_string)) *code = code_table[i].code_byte; } } if (!is_valid_u8(*code)) { asprintf(error, "bad ICMP code %s", code_string); return STATUS_ERR; } return STATUS_OK; }
void output_atoms(struct membuf *out, struct vec *atoms) { struct vec_iterator i[1]; struct vec_iterator i2[1]; struct atom **atomp; struct atom *atom; struct expr **exprp; struct expr *expr; struct membuf *in; const char *p; i32 value; i32 value2; dump_sym_table(LOG_DEBUG, s->sym_table); vec_get_iterator(atoms, i); while((atomp = vec_iterator_next(i)) != NULL) { atom = *atomp; LOG(LOG_DEBUG, ("yadda\n")); switch(atom->type) { case ATOM_TYPE_OP_ARG_NONE: LOG(LOG_DEBUG, ("output: $%02X\n", atom->u.op.code)); membuf_append_char(out, atom->u.op.code); break; case ATOM_TYPE_OP_ARG_U8: /* op with argument */ value = resolve_expr(atom->u.op.arg); if(!is_valid_u8(value)) { LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", value, atom->u.op.code, (void*)atom)); exit(1); } LOG(LOG_DEBUG, ("output: $%02X $%02X\n", atom->u.op.code, value & 255)); membuf_append_char(out, atom->u.op.code); membuf_append_char(out, value); break; case ATOM_TYPE_OP_ARG_I8: /* op with argument */ value = resolve_expr(atom->u.op.arg); if(!is_valid_i8(value)) { LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", value, atom->u.op.code, (void*)atom)); exit(1); } LOG(LOG_DEBUG, ("output: $%02X $%02X\n", atom->u.op.code, value & 255)); membuf_append_char(out, atom->u.op.code); membuf_append_char(out, value); break; case ATOM_TYPE_OP_ARG_UI8: /* op with argument */ value = resolve_expr(atom->u.op.arg); if(!is_valid_ui8(value)) { LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", value, atom->u.op.code, (void*)atom)); exit(1); } LOG(LOG_DEBUG, ("output: $%02X $%02X\n", atom->u.op.code, value & 255)); membuf_append_char(out, atom->u.op.code); membuf_append_char(out, value); break; case ATOM_TYPE_OP_ARG_U16: /* op with argument */ value = resolve_expr(atom->u.op.arg); if(!is_valid_u16(value)) { LOG(LOG_ERROR, ("value %d out of range for op $%02X @%p\n", value, atom->u.op.code, (void*)atom)); exit(1); } value2 = value / 256; value = value % 256; LOG(LOG_DEBUG, ("output: $%02X $%02X $%02X\n", atom->u.op.code, value, value2)); membuf_append_char(out, atom->u.op.code); membuf_append_char(out, value); membuf_append_char(out, value2); break; case ATOM_TYPE_RES: /* reserve memory statement */ value = resolve_expr(atom->u.res.length); if(!is_valid_u16(value)) { LOG(LOG_ERROR, ("length %d for .res(length, value) " "is out of range\n", value)); exit(1); } value2 = resolve_expr(atom->u.res.value); if(!is_valid_ui8(value2)) { LOG(LOG_ERROR, ("value %d for .res(length, value) " "is out of range\n", value)); exit(1); } LOG(LOG_DEBUG, ("output: .RES %d, %d\n", value, value2)); while(--value >= 0) { membuf_append_char(out, value2); } break; case ATOM_TYPE_BUFFER: /* include binary file statement */ value = atom->u.buffer.skip; if(!is_valid_u16(value)) { LOG(LOG_ERROR, ("value %d for .res(length, value) " "is out of range\n", value)); exit(1); } value2 = atom->u.buffer.length; if(!is_valid_u16(value2)) { LOG(LOG_ERROR, ("length %d for .incbin(name, skip, length) " "is out of range\n", value2)); exit(1); } LOG(LOG_DEBUG, ("output: .INCBIN \"%s\", %d, %d\n", atom->u.buffer.name, value, value2)); in = get_named_buffer(s->named_buffer, atom->u.buffer.name); p = membuf_get(in); p += value; while(--value2 >= 0) { membuf_append_char(out, *p++); } break; case ATOM_TYPE_WORD_EXPRS: vec_get_iterator(atom->u.exprs, i2); while((exprp = vec_iterator_next(i2)) != NULL) { expr = *exprp; value = resolve_expr(expr); if(!is_valid_ui16(value)) { LOG(LOG_ERROR, ("value %d for .word(value, ...) " "is out of range\n", value)); } value2 = value / 256; value = value % 256; membuf_append_char(out, value); membuf_append_char(out, value2); } LOG(LOG_DEBUG, ("output: %d words\n", vec_count(atom->u.exprs))); break; case ATOM_TYPE_BYTE_EXPRS: vec_get_iterator(atom->u.exprs, i2); while((exprp = vec_iterator_next(i2)) != NULL) { expr = *exprp; value = resolve_expr(expr); if(!is_valid_ui8(value)) { LOG(LOG_ERROR, ("value %d for .byte(value, ...) " "is out of range\n", value)); } membuf_append_char(out, value); } LOG(LOG_DEBUG, ("output: %d bytes\n", vec_count(atom->u.exprs))); break; default: LOG(LOG_ERROR, ("invalid atom_type %d @%p\n", atom->type, (void*)atom)); exit(1); } } }
struct packet *new_icmp_packet(int address_family, enum direction_t direction, const char *type_string, const char *code_string, int protocol, u32 tcp_start_sequence, u32 payload_bytes, s64 mtu, char **error) { s32 type = -1; /* bad type; means "unknown so far" */ s32 code = -1; /* bad code; means "unknown so far" */ struct packet *packet = NULL; /* the newly-allocated result packet */ /* Calculate lengths in bytes of all sections of the packet. * For now we only support the most common ICMP message * format, which includes at the end the original outgoing IP * header and the first 8 bytes after that (which will * typically have the port info needed to demux the message). */ const int ip_fixed_bytes = ip_header_len(address_family); const int ip_option_bytes = 0; const int ip_header_bytes = ip_fixed_bytes + ip_option_bytes; const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES; const int icmp_bytes = icmp_header_len(address_family) + echoed_bytes; const int ip_bytes = ip_header_bytes + icmp_bytes; /* Sanity-check all the various lengths */ if (ip_option_bytes & 0x3) { asprintf(error, "IP options are not padded correctly " "to ensure IP header is a multiple of 4 bytes: " "%d excess bytes", ip_option_bytes & 0x3); goto error_out; } assert((ip_header_bytes & 0x3) == 0); /* Parse the ICMP type and code */ if (parse_icmp_type_and_code(address_family, type_string, code_string, &type, &code, error)) goto error_out; assert(is_valid_u8(type)); assert(is_valid_u8(code)); /* Allocate and zero out a packet object of the desired size */ packet = packet_new(ip_bytes); memset(packet->buffer, 0, ip_bytes); packet->ip_bytes = ip_bytes; packet->direction = direction; packet->flags = 0; packet->ecn = 0; /* Set IP header fields */ const enum ip_ecn_t ecn = ECN_NONE; set_packet_ip_header(packet, address_family, ip_bytes, direction, ecn, icmp_protocol(address_family)); /* Find the start of the ICMP header and then populate common fields. */ void *icmp_header = packet_start(packet) + ip_header_bytes; if (set_packet_icmp_header(packet, icmp_header, address_family, type, code, mtu, error)) goto error_out; /* All ICMP message types currently supported by this tool * include a copy of the outbound IP header and the first few * bytes inside. To ensure that the inbound ICMP message gets * demuxed to the correct socket in the kernel, here we * construct enough of a basic IP header and during test * execution we fill in the port numbers and (if specified) * TCP sequence number in the TCP header. */ u8 *echoed_ip = packet_echoed_ip_header(packet); const int echoed_ip_bytes = (ip_fixed_bytes + layer4_header_len(protocol) + payload_bytes); set_ip_header(echoed_ip, address_family, echoed_ip_bytes, reverse_direction(direction), ecn, protocol); if (protocol == IPPROTO_TCP) { u32 *seq = packet_echoed_tcp_seq(packet); *seq = htonl(tcp_start_sequence); } return packet; error_out: if (packet != NULL) packet_free(packet); return NULL; }