summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mrouted/icmp.c
blob: 72efa0ac745b2cd14678d144dda672032b3421e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * The mrouted program is covered by the license in the accompanying file
 * named "LICENSE".  Use of the mrouted program represents acceptance of
 * the terms and conditions listed in that file.
 *
 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
 * Leland Stanford Junior University.
 *
 *
 * icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp
 */

#include "defs.h"

#ifndef lint
static char rcsid[] = "@(#) $Id: \
icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp $";
#endif

static int	icmp_socket;

static void	icmp_handler __P((int, fd_set *));
static char *	icmp_name __P((struct icmp *));

void
init_icmp()
{
    if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	log(LOG_ERR, errno, "ICMP socket");

    register_input_handler(icmp_socket, icmp_handler);

    IF_DEBUG(DEBUG_ICMP)
    log(LOG_DEBUG, 0, "registering icmp socket fd %d\n", icmp_socket);
}

static void
icmp_handler(fd, rfds)
    int fd;
    fd_set *rfds;
{
    u_char icmp_buf[RECV_BUF_SIZE];
    struct sockaddr_in from;
    int fromlen, recvlen, iphdrlen, ipdatalen;
    struct icmp *icmp;
    struct ip *ip;
    vifi_t i;
    struct uvif *v;
    u_int32 src;

    fromlen = sizeof(from);
    recvlen = recvfrom(icmp_socket, icmp_buf, RECV_BUF_SIZE, 0,
			    (struct sockaddr *)&from, &fromlen);
    if (recvlen < 0) {
	if (errno != EINTR)
	    log(LOG_WARNING, errno, "icmp_socket recvfrom");
	return;
    }
    ip = (struct ip *)icmp_buf;
    iphdrlen = ip->ip_hl << 2;
#ifdef RAW_INPUT_IS_RAW
    ipdatalen = ntohs(ip->ip_len) - iphdrlen;
#else
    ipdatalen = ip->ip_len;
#endif
    if (iphdrlen + ipdatalen != recvlen) {
	IF_DEBUG(DEBUG_ICMP)
	log(LOG_DEBUG, 0, "hdr %d data %d != rcv %d", iphdrlen, ipdatalen, recvlen);
	/* Malformed ICMP, just return. */
	return;
    }
    if (ipdatalen < ICMP_MINLEN + sizeof(struct ip)) {
	/* Not enough data for us to be interested in it. */
	return;
    }
    src = ip->ip_src.s_addr;
    icmp = (struct icmp *)(icmp_buf + iphdrlen);
    IF_DEBUG(DEBUG_ICMP)
    log(LOG_DEBUG, 0, "got ICMP type %d from %s",
	icmp->icmp_type, inet_fmt(src, s1));
    /*
     * Eventually:
     * have registry of ICMP listeners, by type, code and ICMP_ID
     * (and maybe fields of the original packet too -- maybe need a
     * generalized packet filter!) to allow ping and traceroute
     * from the monitoring tool.
     */
    switch (icmp->icmp_type) {
	case ICMP_UNREACH:
	case ICMP_TIMXCEED:
	    /* Look at returned packet to see if it's us sending on a tunnel */
	    ip = &icmp->icmp_ip;
	    if (ip->ip_p != IPPROTO_IGMP && ip->ip_p != IPPROTO_IPIP)
		return;
	    for (v = uvifs, i = 0; i < numvifs; v++, i++) {
		if (ip->ip_src.s_addr == v->uv_lcl_addr &&
		    ip->ip_dst.s_addr == v->uv_dst_addr) {
		    char *p;
		    int n;
		    /*
		     * I sent this packet on this vif.
		     */
		    n = ++v->uv_icmp_warn;
		    while (n && !(n & 1))
			n >>= 1;
		    if (n == 1 && ((p = icmp_name(icmp)) != NULL))
			log(LOG_WARNING, 0, "Received ICMP %s from %s %s %s on vif %d",
			    p, inet_fmt(src, s1), "for traffic sent to",
			    inet_fmt(ip->ip_dst.s_addr, s2),
			    i);

		    break;
		}
	    }
	    break;
    }
}

/*
 * Return NULL for ICMP informational messages.
 * Return string describing the error for ICMP errors.
 */
static char *
icmp_name(icmp)
    struct icmp *icmp;
{
    static char retval[30];

    switch (icmp->icmp_type) {
	case ICMP_UNREACH:
	    switch (icmp->icmp_code) {
		case ICMP_UNREACH_NET:
		    return "network unreachable";
		case ICMP_UNREACH_HOST:
		    return "host unreachable";
		case ICMP_UNREACH_PROTOCOL:
		    return "protocol unreachable";
		case ICMP_UNREACH_PORT:
		    return "port unreachable";
		case ICMP_UNREACH_NEEDFRAG:
		    return "needs fragmentation";
		case ICMP_UNREACH_SRCFAIL:
		    return "source route failed";
#ifndef ICMP_UNREACH_NET_UNKNOWN
#define ICMP_UNREACH_NET_UNKNOWN	6
#endif
		case ICMP_UNREACH_NET_UNKNOWN:
		    return "network unknown";
#ifndef ICMP_UNREACH_HOST_UNKNOWN
#define ICMP_UNREACH_HOST_UNKNOWN	7
#endif
		case ICMP_UNREACH_HOST_UNKNOWN:
		    return "host unknown";
#ifndef ICMP_UNREACH_ISOLATED
#define ICMP_UNREACH_ISOLATED		8
#endif
		case ICMP_UNREACH_ISOLATED:
		    return "source host isolated";
#ifndef ICMP_UNREACH_NET_PROHIB
#define ICMP_UNREACH_NET_PROHIB		9
#endif
		case ICMP_UNREACH_NET_PROHIB:
		    return "network access prohibited";
#ifndef ICMP_UNREACH_HOST_PROHIB
#define ICMP_UNREACH_HOST_PROHIB	10
#endif
		case ICMP_UNREACH_HOST_PROHIB:
		    return "host access prohibited";
#ifndef ICMP_UNREACH_TOSNET
#define ICMP_UNREACH_TOSNET		11
#endif
		case ICMP_UNREACH_TOSNET:
		    return "bad TOS for net";
#ifndef ICMP_UNREACH_TOSHOST
#define ICMP_UNREACH_TOSHOST		12
#endif
		case ICMP_UNREACH_TOSHOST:
		    return "bad TOS for host";
#ifndef ICMP_UNREACH_FILTER_PROHIB
#define ICMP_UNREACH_FILTER_PROHIB	13
#endif
		case ICMP_UNREACH_FILTER_PROHIB:
		    return "prohibited by filter";
#ifndef ICMP_UNREACH_HOST_PRECEDENCE
#define ICMP_UNREACH_HOST_PRECEDENCE	14
#endif
		case ICMP_UNREACH_HOST_PRECEDENCE:
		    return "host precedence violation";
#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
#define ICMP_UNREACH_PRECEDENCE_CUTOFF	15
#endif
		case ICMP_UNREACH_PRECEDENCE_CUTOFF:
		    return "precedence cutoff";
		default:
		    sprintf(retval, "unreachable code %d", icmp->icmp_code);
		    return retval;
	    }
	case ICMP_SOURCEQUENCH:
	    return "source quench";
	case ICMP_REDIRECT:
	    return NULL;	/* XXX */
	case ICMP_TIMXCEED:
	    switch (icmp->icmp_code) {
		case ICMP_TIMXCEED_INTRANS:
		    return "time exceeded in transit";
		case ICMP_TIMXCEED_REASS:
		    return "time exceeded in reassembly";
		default:
		    sprintf(retval, "time exceeded code %d", icmp->icmp_code);
		    return retval;
	    }
	case ICMP_PARAMPROB:
	    switch (icmp->icmp_code) {
#ifndef ICMP_PARAMPROB_OPTABSENT
#define ICMP_PARAMPROB_OPTABSENT	1
#endif
		case ICMP_PARAMPROB_OPTABSENT:
		    return "required option absent";
		default:
		    sprintf(retval, "parameter problem code %d", icmp->icmp_code);
		    return retval;
	    }
    }
    return NULL;
}
OpenPOWER on IntegriCloud