Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / mrouted / icmp.c
1 /*
2  * The mrouted program is covered by the license in the accompanying file
3  * named "LICENSE".  Use of the mrouted program represents acceptance of
4  * the terms and conditions listed in that file.
5  *
6  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
7  * Leland Stanford Junior University.
8  *
9  *
10  * icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp
11  */
12
13 #include "defs.h"
14
15 #ifndef lint
16 static char rcsid[] = "@(#) $Id: \
17 icmp.c,v 3.8.4.2 1998/01/06 01:57:42 fenner Exp $";
18 #endif
19
20 static int      icmp_socket;
21
22 static void     icmp_handler __P((int, fd_set *));
23 static char *   icmp_name __P((struct icmp *));
24
25 void
26 init_icmp()
27 {
28     if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
29         log(LOG_ERR, errno, "ICMP socket");
30
31     register_input_handler(icmp_socket, icmp_handler);
32
33     IF_DEBUG(DEBUG_ICMP)
34     log(LOG_DEBUG, 0, "registering icmp socket fd %d\n", icmp_socket);
35 }
36
37 static void
38 icmp_handler(fd, rfds)
39     int fd;
40     fd_set *rfds;
41 {
42     u_char icmp_buf[RECV_BUF_SIZE];
43     struct sockaddr_in from;
44     int fromlen, recvlen, iphdrlen, ipdatalen;
45     struct icmp *icmp;
46     struct ip *ip;
47     vifi_t i;
48     struct uvif *v;
49     u_int32 src;
50
51     fromlen = sizeof(from);
52     recvlen = recvfrom(icmp_socket, icmp_buf, RECV_BUF_SIZE, 0,
53                             (struct sockaddr *)&from, &fromlen);
54     if (recvlen < 0) {
55         if (errno != EINTR)
56             log(LOG_WARNING, errno, "icmp_socket recvfrom");
57         return;
58     }
59     ip = (struct ip *)icmp_buf;
60     iphdrlen = ip->ip_hl << 2;
61 #ifdef RAW_INPUT_IS_RAW
62     ipdatalen = ntohs(ip->ip_len) - iphdrlen;
63 #else
64     ipdatalen = ip->ip_len;
65 #endif
66     if (iphdrlen + ipdatalen != recvlen) {
67         IF_DEBUG(DEBUG_ICMP)
68         log(LOG_DEBUG, 0, "hdr %d data %d != rcv %d", iphdrlen, ipdatalen, recvlen);
69         /* Malformed ICMP, just return. */
70         return;
71     }
72     if (ipdatalen < ICMP_MINLEN + sizeof(struct ip)) {
73         /* Not enough data for us to be interested in it. */
74         return;
75     }
76     src = ip->ip_src.s_addr;
77     icmp = (struct icmp *)(icmp_buf + iphdrlen);
78     IF_DEBUG(DEBUG_ICMP)
79     log(LOG_DEBUG, 0, "got ICMP type %d from %s",
80         icmp->icmp_type, inet_fmt(src, s1));
81     /*
82      * Eventually:
83      * have registry of ICMP listeners, by type, code and ICMP_ID
84      * (and maybe fields of the original packet too -- maybe need a
85      * generalized packet filter!) to allow ping and traceroute
86      * from the monitoring tool.
87      */
88     switch (icmp->icmp_type) {
89         case ICMP_UNREACH:
90         case ICMP_TIMXCEED:
91             /* Look at returned packet to see if it's us sending on a tunnel */
92             ip = &icmp->icmp_ip;
93             if (ip->ip_p != IPPROTO_IGMP && ip->ip_p != IPPROTO_IPIP)
94                 return;
95             for (v = uvifs, i = 0; i < numvifs; v++, i++) {
96                 if (ip->ip_src.s_addr == v->uv_lcl_addr &&
97                     ip->ip_dst.s_addr == v->uv_dst_addr) {
98                     char *p;
99                     int n;
100                     /*
101                      * I sent this packet on this vif.
102                      */
103                     n = ++v->uv_icmp_warn;
104                     while (n && !(n & 1))
105                         n >>= 1;
106                     if (n == 1 && ((p = icmp_name(icmp)) != NULL))
107                         log(LOG_WARNING, 0, "Received ICMP %s from %s %s %s on vif %d",
108                             p, inet_fmt(src, s1), "for traffic sent to",
109                             inet_fmt(ip->ip_dst.s_addr, s2),
110                             i);
111
112                     break;
113                 }
114             }
115             break;
116     }
117 }
118
119 /*
120  * Return NULL for ICMP informational messages.
121  * Return string describing the error for ICMP errors.
122  */
123 static char *
124 icmp_name(icmp)
125     struct icmp *icmp;
126 {
127     static char retval[30];
128
129     switch (icmp->icmp_type) {
130         case ICMP_UNREACH:
131             switch (icmp->icmp_code) {
132                 case ICMP_UNREACH_NET:
133                     return "network unreachable";
134                 case ICMP_UNREACH_HOST:
135                     return "host unreachable";
136                 case ICMP_UNREACH_PROTOCOL:
137                     return "protocol unreachable";
138                 case ICMP_UNREACH_PORT:
139                     return "port unreachable";
140                 case ICMP_UNREACH_NEEDFRAG:
141                     return "needs fragmentation";
142                 case ICMP_UNREACH_SRCFAIL:
143                     return "source route failed";
144 #ifndef ICMP_UNREACH_NET_UNKNOWN
145 #define ICMP_UNREACH_NET_UNKNOWN        6
146 #endif
147                 case ICMP_UNREACH_NET_UNKNOWN:
148                     return "network unknown";
149 #ifndef ICMP_UNREACH_HOST_UNKNOWN
150 #define ICMP_UNREACH_HOST_UNKNOWN       7
151 #endif
152                 case ICMP_UNREACH_HOST_UNKNOWN:
153                     return "host unknown";
154 #ifndef ICMP_UNREACH_ISOLATED
155 #define ICMP_UNREACH_ISOLATED           8
156 #endif
157                 case ICMP_UNREACH_ISOLATED:
158                     return "source host isolated";
159 #ifndef ICMP_UNREACH_NET_PROHIB
160 #define ICMP_UNREACH_NET_PROHIB         9
161 #endif
162                 case ICMP_UNREACH_NET_PROHIB:
163                     return "network access prohibited";
164 #ifndef ICMP_UNREACH_HOST_PROHIB
165 #define ICMP_UNREACH_HOST_PROHIB        10
166 #endif
167                 case ICMP_UNREACH_HOST_PROHIB:
168                     return "host access prohibited";
169 #ifndef ICMP_UNREACH_TOSNET
170 #define ICMP_UNREACH_TOSNET             11
171 #endif
172                 case ICMP_UNREACH_TOSNET:
173                     return "bad TOS for net";
174 #ifndef ICMP_UNREACH_TOSHOST
175 #define ICMP_UNREACH_TOSHOST            12
176 #endif
177                 case ICMP_UNREACH_TOSHOST:
178                     return "bad TOS for host";
179 #ifndef ICMP_UNREACH_FILTER_PROHIB
180 #define ICMP_UNREACH_FILTER_PROHIB      13
181 #endif
182                 case ICMP_UNREACH_FILTER_PROHIB:
183                     return "prohibited by filter";
184 #ifndef ICMP_UNREACH_HOST_PRECEDENCE
185 #define ICMP_UNREACH_HOST_PRECEDENCE    14
186 #endif
187                 case ICMP_UNREACH_HOST_PRECEDENCE:
188                     return "host precedence violation";
189 #ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
190 #define ICMP_UNREACH_PRECEDENCE_CUTOFF  15
191 #endif
192                 case ICMP_UNREACH_PRECEDENCE_CUTOFF:
193                     return "precedence cutoff";
194                 default:
195                     sprintf(retval, "unreachable code %d", icmp->icmp_code);
196                     return retval;
197             }
198         case ICMP_SOURCEQUENCH:
199             return "source quench";
200         case ICMP_REDIRECT:
201             return NULL;        /* XXX */
202         case ICMP_TIMXCEED:
203             switch (icmp->icmp_code) {
204                 case ICMP_TIMXCEED_INTRANS:
205                     return "time exceeded in transit";
206                 case ICMP_TIMXCEED_REASS:
207                     return "time exceeded in reassembly";
208                 default:
209                     sprintf(retval, "time exceeded code %d", icmp->icmp_code);
210                     return retval;
211             }
212         case ICMP_PARAMPROB:
213             switch (icmp->icmp_code) {
214 #ifndef ICMP_PARAMPROB_OPTABSENT
215 #define ICMP_PARAMPROB_OPTABSENT        1
216 #endif
217                 case ICMP_PARAMPROB_OPTABSENT:
218                     return "required option absent";
219                 default:
220                     sprintf(retval, "parameter problem code %d", icmp->icmp_code);
221                     return retval;
222             }
223     }
224     return NULL;
225 }