/* * scamper_icmp_resp.c * * $Id: scamper_icmp_resp.c,v 1.9 2007/05/03 23:25:11 mjl Exp $ * * Copyright (C) 2005-2007 The University of Waikato * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #if defined(__APPLE__) #include #endif #include #include #include #include "scamper_addr.h" #include "scamper_task.h" #include "scamper_target.h" #include "scamper_icmp_resp.h" #include "scamper_debug.h" #if !defined(ICMP_UNREACH_FILTER_PROHIB) #if defined(ICMP_UNREACH_ADMIN_PROHIBIT) /* NetBSD */ #define ICMP_UNREACH_FILTER_PROHIB ICMP_UNREACH_ADMIN_PROHIBIT #else #define ICMP_UNREACH_FILTER_PROHIB 13 #endif #endif #if !defined(ICMP6_DST_UNREACH_BEYONDSCOPE) #if defined(ICMP6_DST_UNREACH_NOTNEIGHBOR) #define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR #else #define ICMP6_DST_UNREACH_BEYONDSCOPE 2 #endif #endif #ifndef NDEBUG void scamper_icmp_resp_print(const scamper_icmp_resp_t *ir) { char *type = NULL, tbuf[64]; char *param = NULL, pbuf[64]; char addr[64]; char ip[256]; char icmp[256]; char inner_ip[256]; char inner_transport[256]; assert(ir->ir_af == AF_INET || ir->ir_af == AF_INET6); if(ir->ir_af == AF_INET) { inet_ntop(AF_INET, &ir->ir_ip_src.v4, addr, sizeof(addr)); snprintf(ip, sizeof(ip), "from: %s size %d ttl %d tos 0x%02x ipid 0x%04x", addr, ir->ir_ip_size, ir->ir_ip_ttl, ir->ir_ip_tos, ir->ir_ip_id); switch(ir->ir_icmp_type) { case ICMP_UNREACH: type = "dest unreach"; switch(ir->ir_icmp_code) { case ICMP_UNREACH_NET: param = "code: bad net"; break; case ICMP_UNREACH_HOST: param = "code: bad host"; break; case ICMP_UNREACH_PROTOCOL: param = "code: bad protocol"; break; case ICMP_UNREACH_PORT: param = "code: bad port"; break; case ICMP_UNREACH_SRCFAIL: param = "code: src rt failed"; break; case ICMP_UNREACH_NET_UNKNOWN: param = "code: net unknown"; break; case ICMP_UNREACH_HOST_UNKNOWN: param = "code: host unknown"; break; case ICMP_UNREACH_ISOLATED: param = "code: isolated"; break; case ICMP_UNREACH_NET_PROHIB: param = "code: net prohib"; break; case ICMP_UNREACH_HOST_PROHIB: param = "code: host prohib"; break; case ICMP_UNREACH_TOSNET: param = "code: tos net"; break; case ICMP_UNREACH_TOSHOST: param = "code: tos host"; break; case ICMP_UNREACH_FILTER_PROHIB: param = "code: admin prohib"; break; case ICMP_UNREACH_NEEDFRAG: /* * use the type buf to be consistent with the ICMP6 * fragmentation required message */ snprintf(tbuf, sizeof(tbuf), "need frag: %d", ir->ir_icmp_nhmtu); type = tbuf; break; default: snprintf(pbuf, sizeof(pbuf), "code: %d", ir->ir_icmp_code); param = pbuf; break; } break; case ICMP_TIMXCEED: type = "time exceeded"; switch(ir->ir_icmp_code) { case ICMP_TIMXCEED_INTRANS: param = "code: ttl exp trans"; break; case ICMP_TIMXCEED_REASS: param = "code: ttl exp reass"; break; default: snprintf(pbuf, sizeof(pbuf), "code: %d", ir->ir_icmp_code); param = pbuf; break; } break; case ICMP_ECHOREPLY: type = "echo reply"; snprintf(pbuf, sizeof(pbuf), "id: %d seq: %d", ir->ir_icmp_id, ir->ir_icmp_seq); param = pbuf; break; } } else /* if(ir->ir_af == AF_INET6) */ { inet_ntop(AF_INET6, &ir->ir_ip_src.v6, addr, sizeof(addr)); snprintf(ip, sizeof(ip), "from: %s size %d hlim %d", addr, ir->ir_ip_size, ir->ir_ip_hlim); switch(ir->ir_icmp_type) { case ICMP6_DST_UNREACH: type = "dest unreach"; switch(ir->ir_icmp_code) { case ICMP6_DST_UNREACH_NOROUTE: param = "code: no route"; break; case ICMP6_DST_UNREACH_ADMIN: param = "code: admin prohib"; break; case ICMP6_DST_UNREACH_BEYONDSCOPE: param = "code: beyond scope"; break; case ICMP6_DST_UNREACH_ADDR: param = "code: addr unreach"; break; case ICMP6_DST_UNREACH_NOPORT: param = "code: port unreach"; break; default: snprintf(pbuf, sizeof(pbuf), "code: %d", ir->ir_icmp_code); param = pbuf; break; } break; case ICMP6_TIME_EXCEEDED: type = "time exceeded"; switch(ir->ir_icmp_code) { case ICMP6_TIME_EXCEED_TRANSIT: param = "code: ttl exp trans"; break; case ICMP6_TIME_EXCEED_REASSEMBLY: param = "code: ttl exp reass"; break; default: snprintf(pbuf, sizeof(pbuf), "code: %d", ir->ir_icmp_code); param = pbuf; break; } break; case ICMP6_PACKET_TOO_BIG: snprintf(tbuf, sizeof(tbuf), "need frag: %d", ir->ir_icmp_nhmtu); type = tbuf; break; case ICMP6_ECHO_REPLY: type = "echo reply"; snprintf(pbuf, sizeof(pbuf), "id: %d seq: %d", ir->ir_icmp_id, ir->ir_icmp_seq); param = pbuf; break; } } if(type == NULL) { snprintf(icmp, sizeof(icmp), "icmp: %d code: %d", ir->ir_icmp_type, ir->ir_icmp_code); } else if(param == NULL) { snprintf(icmp, sizeof(icmp), "icmp: %s", type); } else { snprintf(icmp, sizeof(icmp), "icmp: %s %s", type, param); } if(ir->ir_flags & SCAMPER_ICMP_RESP_FLAG_INNER_IP) { if(ir->ir_af == AF_INET) { inet_ntop(AF_INET, &ir->ir_inner_ip_dst.v4, addr, sizeof(addr)); snprintf(inner_ip, sizeof(inner_ip), " to: %s size %d ttl %d tos 0x%02x ipid 0x%04x", addr, ir->ir_inner_ip_size, ir->ir_inner_ip_ttl, ir->ir_inner_ip_tos, ir->ir_inner_ip_id); } else /* if(ir->ir_af == AF_INET6) */ { inet_ntop(AF_INET6, &ir->ir_inner_ip_dst.v6, addr, sizeof(addr)); snprintf(inner_ip, sizeof(inner_ip), " to: %s size %d hlim %d flow 0x%05x", addr, ir->ir_inner_ip_size, ir->ir_inner_ip_hlim, ir->ir_inner_ip_flow); } switch(ir->ir_inner_ip_proto) { case IPPROTO_UDP: snprintf(inner_transport, sizeof(inner_transport), " proto: UDP sport %d dport %d", ir->ir_inner_udp_sport, ir->ir_inner_udp_dport); break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: snprintf(inner_transport, sizeof(inner_transport), " proto: ICMP type %d code %d id %04x seq %04x", ir->ir_inner_icmp_type, ir->ir_inner_icmp_code, ir->ir_inner_icmp_id, ir->ir_inner_icmp_seq); break; case IPPROTO_TCP: snprintf(inner_transport, sizeof(inner_transport), " proto: TCP sport %d dport %d seq %08x", ir->ir_inner_tcp_sport, ir->ir_inner_tcp_dport, ir->ir_inner_tcp_seq); break; default: inner_transport[0] = '\0'; break; } } else { inner_ip[0] = '\0'; inner_transport[0] = '\0'; } scamper_debug(NULL, "%s %s%s%s", ip, icmp, inner_ip, inner_transport); return; } #endif int scamper_icmp_resp_src(scamper_icmp_resp_t *resp, scamper_addr_t *addr) { if(resp->ir_af == AF_INET) { addr->type = SCAMPER_ADDR_TYPE_IPV4; addr->addr = &resp->ir_ip_src.v4; return 0; } if(resp->ir_af == AF_INET6) { addr->type = SCAMPER_ADDR_TYPE_IPV6; addr->addr = &resp->ir_ip_src.v6; return 0; } return -1; } int scamper_icmp_resp_inner_dst(scamper_icmp_resp_t *resp,scamper_addr_t *addr) { if(resp->ir_af == AF_INET) { addr->type = SCAMPER_ADDR_TYPE_IPV4; addr->addr = &resp->ir_inner_ip_dst.v4; return 0; } if(resp->ir_af == AF_INET6) { addr->type = SCAMPER_ADDR_TYPE_IPV6; addr->addr = &resp->ir_inner_ip_dst.v6; return 0; } return -1; } void scamper_icmp_resp_handle(scamper_icmp_resp_t *resp) { scamper_target_t *target; scamper_task_t *task; scamper_addr_t addr; /* the target address of the probe is embedded in the response */ if(SCAMPER_ICMP_RESP_IS_TTL_EXP(resp) || SCAMPER_ICMP_RESP_IS_UNREACH(resp) || SCAMPER_ICMP_RESP_IS_PACKET_TOO_BIG(resp)) { /* not able to handle this response, drop it */ if(!SCAMPER_ICMP_RESP_INNER_IS_SET(resp)) { return; } if(scamper_icmp_resp_inner_dst(resp, &addr) != 0) { return; } } else if(SCAMPER_ICMP_RESP_IS_ECHO_REPLY(resp)) { if(scamper_icmp_resp_src(resp, &addr) != 0) { return; } } else { return; } if((target = scamper_target_find(&addr)) == NULL) { return; } task = target->task; if(task->funcs->handle_icmp != NULL) { task->funcs->handle_icmp(task, resp); } return; }