1 /*
2 * $Id: write_ip.c,v 1.3 1999/06/24 18:35:34 dugsong Exp $
3 *
4 * libnet
5 * write_ip.c - writes a prebuilt IP packet to the network
6 *
7 * Copyright (c) 1998, 1999 Mike D. Schiffman <mike@infonexus.com>
8 * route|daemon9 <route@infonexus.com>
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34 #if (HAVE_CONFIG_H)
35 #include "../include/config.h"
36 #endif
37 #include "../include/libnet.h"
38
39 int
write_ip(int sock,u_char * buf,int len)40 write_ip(int sock, u_char *buf, int len)
41 {
42 int c;
43 struct sockaddr_in sin;
44 struct ip *ip_hdr;
45
46 #if (RAW_IS_COOKED)
47 return write_ip_via_datalink(buf,len);
48 #endif /* RAW_IS_COOKED */
49
50 ip_hdr = (struct ip *)buf;
51
52 #if (BSD_BYTE_SWAP)
53 /*
54 * For link access, we don't need to worry about the inconsistencies of
55 * certain BSD kernels. However, raw socket nuances abound. Certain
56 * BSD implmentations require the ip_len and ip_off fields to be in host
57 * byte order. It's MUCH easier to change it here than inside the bpf
58 * writing routine.
59 */
60 ip_hdr->ip_len = FIX(ip_hdr->ip_len);
61 ip_hdr->ip_off = FIX(ip_hdr->ip_off);
62 #endif
63
64 memset(&sin, 0, sizeof(struct sockaddr_in));
65 sin.sin_family = AF_INET;
66 sin.sin_addr.s_addr = ip_hdr->ip_dst.s_addr;
67
68 c = sendto(sock, buf, len, 0, (struct sockaddr *)&sin,
69 sizeof(struct sockaddr));
70 if (c != len)
71 {
72 #if (__DEBUG)
73 fprintf(stderr, "write_ip: %d bytes written (%s)\n", c, strerror(errno));
74 #endif
75 }
76 #if (BSD_BYTE_SWAP)
77 ip_hdr->ip_len = UNFIX(ip_hdr->ip_len);
78 ip_hdr->ip_off = UNFIX(ip_hdr->ip_off);
79 #endif
80
81 return (c);
82 }
83
84 #if (RAW_IS_COOKED)
85 /*
86 * write_ip_via_datalink
87 *
88 * The intent of this function is to provide an easy mechanism for someone
89 * to send an IP packet that is inserted into the network via the datalink
90 * access interfaces. This is useful in situations where raw sockets are
91 * cooked, which can prevent programs using write_ip from working on certain
92 * Unixes.
93 * To pull this off, we have to determine a source and destination MAC
94 * address for the packet, as well as the correct interface to send it on.
95 *
96 * This has currently only been implemented and tested on solaris 2.6/2.5.1.
97 */
98
99 int
write_ip_via_datalink(u_char * buf,int len)100 write_ip_via_datalink(u_char *buf, int len)
101 {
102 char ebuf[1024];
103 const u_char *broadcast_hwaddr="\xff\xff\xff\xff\xff\xff";
104
105 static u_long cached_dest_ip=0;
106 static u_char *cached_device=NULL;
107 static struct link_int *cached_linkent=NULL;
108
109 struct link_int *linkent=NULL;
110 u_long local_ip;
111 u_long dest_ip;
112
113 u_char *sbuf;
114 static u_char source_hwaddr[6];
115 static u_char dest_hwaddr[6];
116 u_long gateway_ip;
117 u_char *device;
118
119 struct ip *ip_hdr;
120 int c;
121
122 ip_hdr = (struct ip *)(buf);
123 dest_ip = ip_hdr->ip_dst.s_addr;
124
125 #if (__DEBUG)
126 fprintf(stderr, "entering write_ip_via_datalink\n");
127 #endif
128
129 if (cached_dest_ip == dest_ip)
130 {
131 #if (__DEBUG)
132 fprintf(stderr, "taking cached shortcut\n");
133 #endif
134 linkent=cached_linkent;
135 device=cached_device;
136 goto send_it;
137 }
138
139 /*
140 * Step one: Pick the local interface to send the packet on.
141 *
142 * This requires support for the udp socket trick. If it isn't there
143 * then we will just pick the first available interface and try it out.
144 */
145
146 local_ip=libnet_get_interface_by_dest(dest_ip,ebuf);
147
148 if (local_ip==-1)
149 {
150 fprintf(stderr,"libnet_get_interface_by_dest: %s\n",ebuf);
151 return -1;
152 }
153 if (local_ip==0) /* We must be in Solaris 2.5.1 or lower. */
154 {
155 /*struct sockaddr_in tempsin;*/
156
157 #if (__DEBUG)
158 fprintf(stderr, "libnet_get_interface_by_dest is not supported\n");
159 #endif
160 /* XXX - This function is currently broken under Solaris 2.5.1
161 if (libnet_select_device(&tempsin,&device,ebuf)==-1)
162 {
163 fprintf(stderr,"libnet_select_device: %s\n",ebuf);
164 return -1;
165 }
166 local_ip=tempsin.sin_addr.s_addr;*/
167
168 /* Instead, we will try a couple of devices and see what happens. */
169
170 if ((linkent=libnet_open_link_interface("le0",ebuf))==NULL)
171 {
172 if ((linkent=libnet_open_link_interface("hme0",ebuf))==NULL)
173 {
174 fprintf(stderr,"I can't find the interface to use.\n");
175 return -1;
176 }
177 else
178 device="hme0";
179 }
180 else
181 device="le0";
182
183 if ((local_ip=get_ipaddr(linkent,device,ebuf))==-1)
184 {
185 fprintf(stderr,"get_ipaddr: %s\n",ebuf);
186 return -1;
187 }
188 }
189 else
190 {
191 if (libnet_select_device_by_ip(&device,local_ip,ebuf)==-1)
192 {
193 fprintf(stderr,"libnet_select_device_by_ip: %s\n",ebuf);
194 return -1;
195 }
196 }
197
198 #if (__DEBUG)
199 fprintf(stderr, "Using device %s with local IP of %s\n",device,
200 host_lookup(local_ip,0));
201 #endif
202
203 /*
204 * Step two: Get the source MAC address.
205 */
206
207 if (libnet_query_arp(local_ip,source_hwaddr,ebuf)==-1)
208 {
209 fprintf(stderr,"libnet_query_arp_cache: %s\n",ebuf);
210 return -1;
211 }
212
213 #if (__DEBUG)
214 fprintf(stderr, "Using source MAC address %x:%x:%x:%x:%x:%x\n",
215 source_hwaddr[0], source_hwaddr[1], source_hwaddr[2],
216 source_hwaddr[3], source_hwaddr[4], source_hwaddr[5]);
217 #endif
218
219 /*
220 * Step three: Get the destination MAC address.
221 *
222 * To really do this right, we use a routing socket to query the kernel
223 * for the appropriate gateway. If it is our own interface then we need
224 * to do an ARP resolution of the target IP. Otherwise, we need to do
225 * an ARP resolution of the gateway IP. If there is no support for routing
226 * sockets then we can use the broadcast MAC address which seems to work
227 * fine.
228 */
229
230 if ((gateway_ip=libnet_get_next_hop(dest_ip, ebuf))==-1)
231 {
232 #if (__DEBUG)
233 fprintf(stderr, "libnet_get_next_hop failed: %s\n",ebuf);
234 #endif
235 memcpy(dest_hwaddr,broadcast_hwaddr,6);
236 }
237 else
238 {
239 if (gateway_ip==local_ip)
240 {
241 /* This is on our subnet. We need to request the ARP address of
242 * the destination IP. */
243 if (libnet_active_query_arp(dest_ip,dest_hwaddr,device,ebuf)==-1)
244 {
245 #if (__DEBUG)
246 fprintf(stderr, "libnet_query_arp failed: %s\n",ebuf);
247 #endif
248 memcpy(dest_hwaddr,broadcast_hwaddr,6);
249 }
250 }
251 else /* We need to send to a gateway. */
252 {
253 if (libnet_active_query_arp(gateway_ip,dest_hwaddr,device,ebuf)==-1)
254 {
255 #if (__DEBUG)
256 fprintf(stderr, "libnet_query_arp failed: %s\n",ebuf);
257 #endif
258 memcpy(dest_hwaddr,broadcast_hwaddr,6);
259 }
260 }
261 }
262
263 #if (__DEBUG)
264 fprintf(stderr, "Using destination MAC address %x:%x:%x:%x:%x:%x\n",
265 dest_hwaddr[0], dest_hwaddr[1], dest_hwaddr[2],
266 dest_hwaddr[3], dest_hwaddr[4], dest_hwaddr[5]);
267 #endif
268
269 /*
270 * Step four: ship it.
271 */
272
273 if (!linkent && (linkent=libnet_open_link_interface(device,ebuf))==NULL)
274 {
275 fprintf(stderr,"libnet_open_link_interface: %s\n",ebuf);
276 return -1;
277 }
278
279 send_it:
280
281 if (libnet_init_packet(ETH_H + len, &sbuf)==-1)
282 {
283 fprintf(stderr,"libnet_init_packet: %s\n",ebuf);
284 return -1;
285 }
286
287 libnet_build_ethernet(dest_hwaddr,source_hwaddr,ETHERTYPE_IP,NULL,0,sbuf);
288
289 memcpy(sbuf+ETH_H,buf,len);
290 libnet_do_checksum(sbuf+ETH_H, IPPROTO_IP, ip_hdr->ip_hl*4);
291 if ((c=libnet_write_link_layer(linkent,device,sbuf,ETH_H+len))<0)
292 {
293 fprintf(stderr,"libnet_write_link_later: %s\n",strerror(errno));
294 return -1;
295 }
296
297 cached_dest_ip=dest_ip;
298 cached_device=device;
299 if (cached_linkent && (cached_linkent != linkent))
300 {
301 libnet_close_link_interface(cached_linkent);
302 }
303 cached_linkent=linkent;
304
305 libnet_destroy_packet(&sbuf);
306
307 if (c != len +ETH_H)
308 {
309 #if (__DEBUG)
310 fprintf(stderr, "write_ip_via_datalink: %d bytes written (%s)\n", c, strerror(errno));
311 #endif
312 }
313
314 return (c-ETH_H);
315
316 }
317 #endif /* RAW_IS_COOKED */
318
319 /* EOF */
320