char *icmp_err_2_str(struct icmp_err const *err, unsigned set_values) { char *str = tempstr(); snprintf(str, TEMPSTR_SIZE, "protocol=%"PRIu8", src=%s:%"PRIu16", dst=%s:%"PRIu16, err->protocol, ip_addr_2_str(err->addr+0), set_values & ICMP_ERR_PORT_SET ? err->port[0] : 0, ip_addr_2_str(err->addr+1), set_values & ICMP_ERR_PORT_SET ? err->port[1] : 0); return str; }
void spawn_rtp_subparsers(struct ip_addr const *this_host, uint16_t this_port, struct ip_addr const *other_host, uint16_t other_port, struct timeval const *now, struct proto *requestor) { SLOG(LOG_DEBUG, "Spawning RT(C)P parsers for %s:%"PRIu16"<->%s:%"PRIu16, ip_addr_2_str(this_host), this_port, ip_addr_2_str(other_host), other_port); (void)cnxtrack_ip_new(IPPROTO_UDP, this_host, this_port, other_host, other_port, false, proto_rtp, now, requestor); (void)cnxtrack_ip_new(IPPROTO_UDP, this_host, this_port+1, other_host, other_port+1, false, proto_rtcp, now, requestor); }
static int sdp_extract_host(unsigned unused_ cmd, struct liner *liner, void *info_) { struct sdp_proto_info *info = info_; #define IN_IP "IN IP" #define IN_IP_LEN strlen(IN_IP) if (liner_tok_length(liner) < IN_IP_LEN) return -1; if (strncasecmp(liner->start, IN_IP, IN_IP_LEN)) return -1; char const *start = liner->start + IN_IP_LEN; int version = start[0] - '0'; if (version != 4 && version != 6) { SLOG(LOG_DEBUG, "Bogus IP version (%d)", version); return -1; } struct liner space_liner; liner_init(&space_liner, &delim_spaces, (char const *)start, liner_tok_length(liner) - IN_IP_LEN); liner_next(&space_liner); // skipping the IP version number #undef IN_IP #undef IN_IP_LEN if (0 != ip_addr_ctor_from_str(&info->host, space_liner.start, liner_tok_length(&space_liner), version)) return -1; info->set_values |= SDP_HOST_SET; SLOG(LOG_DEBUG, "host found (%s)", ip_addr_2_str(&info->host)); return 0; }
char const *ip_addr_2_strv6(struct ip_addr const *addr) { if (ip_addr_is_v6(addr)) return ip_addr_2_str(addr); char *str = tempstr(); snprintf(str, TEMPSTR_SIZE, "::ffff:%"PRINIPQUAD, NIPQUAD(&addr->u.v4)); return str; }
static char const *via_2_str(struct sip_via const *via) { char *str = tempstr(); snprintf(str, TEMPSTR_SIZE, "%s %s:%"PRIu16, via_protocol_2_str(via->protocol), ip_addr_2_str(&via->addr), via->port); return str; }
static char const *sdp_info_2_str(struct proto_info const *info_) { struct sdp_proto_info const *info = DOWNCAST(info_, info, sdp_proto_info); char *str = tempstr(); snprintf(str, TEMPSTR_SIZE, "%s, host=%s, port=%s", proto_info_2_str(info_), info->set_values & SDP_HOST_SET ? ip_addr_2_str(&info->host) : "unset", info->set_values & SDP_PORT_SET ? tempstr_printf("%u", info->port) : "unset"); return str; }
static char const *dhcp_info_2_str(struct proto_info const *info_) { struct dhcp_proto_info const *info = DOWNCAST(info_, info, dhcp_proto_info); char *str = tempstr(); snprintf(str, TEMPSTR_SIZE, "%s, opcode=%s, msg_type=%s, xid=0x%x, client_ip=%s, client_mac=%s, server=%s", proto_info_2_str(info_), dhcp_opcode_2_str(info->opcode), dhcp_msg_type_2_str(info->msg_type), info->xid, info->set_values & DHCP_CLIENT_SET ? ip_addr_2_str(&info->client) : "unset", info->hw_addr_is_eth ? eth_addr_2_str(info->client_mac) : "not eth", info->server_name[0] != '\0' ? info->server_name : "unset"); return str; }
// The Via header may inform us that the peer is expecting answers on a non-standard port. Let's conntrack it. static void conntrack_via(struct sip_proto_info const *info, struct timeval const *now) { /* We conntrack the Via at every occurence, which will insert many conntrack * that will never be used (because most of the time Via is toward default SIP * port anyway. * We can't but hope that timeouting tracked connection will compensate for our * slopyness. * Notice that we conntrack from any peer to the Via address. SIP is so fun! */ SLOG(LOG_DEBUG, "Conntracking SIP via %s %s:%"PRIu16, info->via.protocol == IPPROTO_UDP ? "UDP" : info->via.protocol == IPPROTO_TCP ? "TCP" : "unknown", ip_addr_2_str(&info->via.addr), info->via.port); assert(info->set_values & SIP_VIA_SET); (void)cnxtrack_ip_new(info->via.protocol, &info->via.addr, info->via.port, ADDR_UNKNOWN, PORT_UNKNOWN, false /* only one cnx */, proto_sip, now, NULL); }
static void spawn_subparsers(struct ip_addr const *this_host, uint16_t this_port, struct ip_addr const *other_host, uint16_t other_port, struct proto_layer *parent, struct timeval const *now) { ASSIGN_LAYER_AND_INFO_OPT(ip, parent); if (! ip) return; SLOG(LOG_DEBUG, "Spawning RT(C)P parsers for %s:%"PRIu16"<->%s:%"PRIu16, ip_addr_2_str(this_host), this_port, ip_addr_2_str(other_host), other_port); unsigned way2; struct mux_subparser *udp_parser = ip_subparser_lookup(layer_ip->parser, proto_udp, NULL, IPPROTO_UDP, this_host, other_host, &way2, now); if (! udp_parser) return; // Notice that we request RT(C)P on behalf of our parent (void)udp_subparser_and_parser_new(udp_parser->parser, proto_rtp, parent->parser, this_port, other_port, way2, now); // rtp conntrack (void)udp_subparser_and_parser_new(udp_parser->parser, proto_rtcp, parent->parser, this_port+1, other_port+1, way2, now); // rtcp conntrack }
static void ip_addr_ctor_from_str_check(void) { static struct { char const *str; int mode; } const tests[] = { { "0.0.0.0", 4, }, { "1.2.3.4", 4, }, { "0.0.0.1", 4, }, { "128.2.1.255", 4, }, { "::ffff:1.2.3.4", 6, }, }; for (unsigned t = 0; t < NB_ELEMS(tests); t++) { struct ip_addr addr; ip_addr_ctor_from_str(&addr, tests[t].str, strlen(tests[t].str), tests[t].mode ); char const *str = ip_addr_2_str(&addr); SLOG(LOG_DEBUG, "Comparing '%s' with '%s'", tests[t].str, str); assert(0 == strcmp(str, tests[t].str)); } }
static enum proto_parse_status sdp_parse(struct parser *parser, struct proto_layer *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, proto_okfn_t *okfn) { struct sdp_parser *sdp_parser = DOWNCAST(parser, parser, sdp_parser); static struct sdper_field const fields[] = { { 1, "c", sdp_extract_host }, { 1, "m", sdp_extract_port }, }; static struct sdper const sdper = { .nb_fields = NB_ELEMS(fields), .fields = fields }; SLOG(LOG_DEBUG, "Starting SDP analysis"); /* Parse */ struct sdp_proto_info info; sdp_proto_info_ctor(&info, wire_len, 0); struct proto_layer layer; proto_layer_ctor(&layer, parent, parser, &info.info); if (0 != sdper_parse(&sdper, &cap_len, packet, cap_len, &info)) return PROTO_PARSE_ERR; if ( (info.set_values & SDP_PORT_SET) && (info.set_values & SDP_HOST_SET) ) { SLOG(LOG_DEBUG, "SDP@%p, connect info is %s:%"PRIu16, sdp_parser, ip_addr_2_str(&info.host), info.port); if (! sdp_parser->host_set) { sdp_parser->host_set = true; sdp_parser->host = info.host; sdp_parser->port = info.port; ASSIGN_LAYER_AND_INFO_OPT(ip, parent); if (layer_ip) { sdp_parser->sender = ip->key.addr[0]; sdp_parser->sender_set = true; } else { sdp_parser->sender_set = false; } } else if (0 != ip_addr_cmp(&sdp_parser->host, &info.host)) { // Start conntracking between the advertized hosts spawn_subparsers(&sdp_parser->host, sdp_parser->port, &info.host, info.port, parent, now); ASSIGN_LAYER_AND_INFO_OPT(ip, parent); bool may_use_stun[2] = { 0 != ip_addr_cmp(&sdp_parser->sender, &sdp_parser->host), ip && 0 != ip_addr_cmp(&ip->key.addr[0], &info.host), }; // If the sender IP was different from the advertized host, start conntracking on this socket also if (may_use_stun[0]) { spawn_subparsers(&sdp_parser->sender, sdp_parser->port, &info.host, info.port, parent, now); } // If _this_ sender IP is different from this advertized host, start conntracking on this socket as well if (may_use_stun[1]) { spawn_subparsers(&sdp_parser->host, sdp_parser->port, &ip->key.addr[0], info.port, parent, now); } // If both senders IP were different from advertized ones then start conntracking between these two senders IP as well if (may_use_stun[0] && may_use_stun[1]) { spawn_subparsers(&sdp_parser->sender, sdp_parser->port, &ip->key.addr[0], info.port, parent, now); } // TODO: terminate this parser. meanwhile, reset its state : sdp_parser->host_set = false; sdp_parser->sender_set = false; } } return proto_parse(NULL, &layer, way, NULL, 0, 0, now, okfn); }
static char const *skinny_info_2_str(struct proto_info const *info_) { struct skinny_proto_info const *info = DOWNCAST(info_, info, skinny_proto_info); return tempstr_printf("%s, HeaderVer:%s, MsgId:%s%s%s%s%s%s%s%s%s%s", proto_info_2_str(&info->info), skinny_header_version_2_str(info->header_ver), skinny_msgid_2_str(info->msgid), info->set_values & SKINNY_NEW_KEY_PAD ? tempstr_printf(", Key:%"PRIu32, info->new_key_pad):"", info->set_values & SKINNY_LINE_INSTANCE ? tempstr_printf(", Line:%"PRIu32, info->line_instance):"", info->set_values & SKINNY_CALL_ID ? tempstr_printf(", CallId:%"PRIu32, info->call_id):"", info->set_values & SKINNY_CONFERENCE_ID ? tempstr_printf(", ConfId:%"PRIu32, info->conf_id):"", info->set_values & SKINNY_PASS_THRU_ID ? tempstr_printf(", PassThruId:%"PRIu32, info->pass_thru_id):"", info->set_values & SKINNY_CALL_STATE ? tempstr_printf(", CallState:%s", skinny_call_state_2_str(info->call_state)):"", info->set_values & SKINNY_MEDIA_CNX ? tempstr_printf(", MediaIp:%s:%"PRIu16, ip_addr_2_str(&info->media_ip), info->media_port):"", info->set_values & SKINNY_CALLING_PARTY ? tempstr_printf(", CallingParty:%s", info->calling_party):"", info->set_values & SKINNY_CALLED_PARTY ? tempstr_printf(", CalledParty:%s", info->called_party):""); }
static enum proto_parse_status sdp_parse(struct parser *parser, struct proto_info *parent, unsigned way, uint8_t const *packet, size_t cap_len, size_t wire_len, struct timeval const *now, size_t tot_cap_len, uint8_t const *tot_packet) { struct sdp_parser *sdp_parser = DOWNCAST(parser, parser, sdp_parser); static struct sdper_field const fields[] = { { 1, "c", sdp_extract_host }, { 1, "m", sdp_extract_port }, }; static struct sdper const sdper = { .nb_fields = NB_ELEMS(fields), .fields = fields }; SLOG(LOG_DEBUG, "Starting SDP analysis"); /* Parse */ struct sdp_proto_info info; sdp_proto_info_ctor(&info, parser, parent, wire_len, 0); if (0 != sdper_parse(&sdper, &cap_len, packet, cap_len, &info)) return PROTO_PARSE_ERR; // Start conntracking of RT(C)P streams if we have all required informations if ( (info.set_values & SDP_PORT_SET) && (info.set_values & SDP_HOST_SET) ) { SLOG(LOG_DEBUG, "SDP@%p, connect info is %s:%"PRIu16, sdp_parser, ip_addr_2_str(&info.host), info.port); /* FIXME: store both peers of the SDP tunnel and respawn the RT(C)Ps as soon as * one of the end changes. Problem is: we don't know which peer this is! */ if (! sdp_parser->host_set) { sdp_parser->host_set = true; sdp_parser->host = info.host; sdp_parser->port = info.port; ASSIGN_INFO_OPT(ip, parent); if (ip) { sdp_parser->sender = ip->key.addr[0]; sdp_parser->sender_set = true; } else { sdp_parser->sender_set = false; } } else if (0 != ip_addr_cmp(&sdp_parser->host, &info.host)) { /* Start conntracking between the advertized hosts * Notice that we request RT(C)P on behalf of our parent! */ spawn_rtp_subparsers(&sdp_parser->host, sdp_parser->port, &info.host, info.port, now, parent->parser->proto); ASSIGN_INFO_OPT(ip, parent); bool may_use_stun[2] = { 0 != ip_addr_cmp(&sdp_parser->sender, &sdp_parser->host), ip && 0 != ip_addr_cmp(&ip->key.addr[0], &info.host), }; // If the sender IP was different from the advertized host, start conntracking on this socket too if (may_use_stun[0]) { spawn_rtp_subparsers(&sdp_parser->sender, sdp_parser->port, &info.host, info.port, now, parent->parser->proto); } // If _this_ sender IP is different from this advertized host, start conntracking on this socket as well if (may_use_stun[1]) { spawn_rtp_subparsers(&sdp_parser->host, sdp_parser->port, &ip->key.addr[0], info.port, now, parent->parser->proto); } // If both senders IP were different from advertized ones then start conntracking between these two senders IP as well if (may_use_stun[0] && may_use_stun[1]) { spawn_rtp_subparsers(&sdp_parser->sender, sdp_parser->port, &ip->key.addr[0], info.port, now, parent->parser->proto); } // TODO: terminate this parser. meanwhile, reset its state : sdp_parser->host_set = false; sdp_parser->sender_set = false; } } return proto_parse(NULL, &info.info, way, NULL, 0, 0, now, tot_cap_len, tot_packet); }