/* * Copyright (c) 1999, 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * bootp_transmit.c * - send a bootp reques using a socket or BPF */ /* * Modification History * * May 11, 2000 Dieter Siegmund (dieter@apple.com) * - created */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bootp_transmit.h" #include "bpflib.h" #include "in_cksum.h" extern void my_log(int priority, const char *message, ...); static char link_broadcast[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; typedef struct { struct ip ip; struct udphdr udp; } ip_udp_header_t; typedef struct { struct in_addr src_ip; struct in_addr dest_ip; char zero; char proto; unsigned short length; } udp_pseudo_hdr_t; static int get_bpf_fd(char * if_name) { int bpf_fd; bpf_fd = bpf_new(); if (bpf_fd < 0) { /* BPF transmit unavailable */ my_log(LOG_ERR, "Transmitter: bpf_fd() failed, %s (%d)", strerror(errno), errno); } else if (bpf_filter_receive_none(bpf_fd) < 0) { my_log(LOG_ERR, "Transmitter: failed to set filter, %s (%d)", strerror(errno), errno); bpf_dispose(bpf_fd); bpf_fd = -1; } else if (bpf_setif(bpf_fd, if_name) < 0) { my_log(LOG_ERR, "Transmitter: bpf_setif(%s) failed: %s (%d)", if_name, strerror(errno), errno); bpf_dispose(bpf_fd); bpf_fd = -1; } return (bpf_fd); } int bootp_transmit(int sockfd, char sendbuf[2048], char * if_name, int hwtype, void * hwaddr, int hwlen, struct in_addr dest_ip, struct in_addr src_ip, u_short dest_port, u_short src_port, void * data, int len) { static int first = 1; static int ip_id = 0; int bpf_fd = -1; int status = 0; if (first) { first = 0; ip_id = random(); } if ((hwtype == ARPHRD_ETHER || hwtype == ARPHRD_IEEE1394) && (ntohl(dest_ip.s_addr) == INADDR_BROADCAST || hwaddr != NULL)) { bpf_fd = get_bpf_fd(if_name); if (bpf_fd < 0) { status = -1; } else { int frame_length; ip_udp_header_t * ip_udp; char * payload; udp_pseudo_hdr_t * udp_pseudo; switch (hwtype) { default: case ARPHRD_ETHER: { struct ether_header * eh_p; eh_p = (struct ether_header *)sendbuf; ip_udp = (ip_udp_header_t *)(sendbuf + sizeof(*eh_p)); udp_pseudo = (udp_pseudo_hdr_t *)(((char *)&ip_udp->udp) - sizeof(*udp_pseudo)); payload = sendbuf + sizeof(*eh_p) + sizeof(*ip_udp); /* fill in the ethernet header */ if (ntohl(dest_ip.s_addr) == INADDR_BROADCAST) { bcopy(link_broadcast, eh_p->ether_dhost, sizeof(eh_p->ether_dhost)); } else { bcopy(hwaddr, eh_p->ether_dhost, sizeof(eh_p->ether_dhost)); } eh_p->ether_type = htons(ETHERTYPE_IP); frame_length = sizeof(*eh_p) + sizeof(*ip_udp) + len; break; } case ARPHRD_IEEE1394: { struct firewire_header * fh_p; /* fill in the firewire header */ fh_p = (struct firewire_header *)sendbuf; bcopy(link_broadcast, fh_p->firewire_dhost, sizeof(fh_p->firewire_dhost)); fh_p->firewire_type = htons(ETHERTYPE_IP); ip_udp = (ip_udp_header_t *)(sendbuf + sizeof(*fh_p)); udp_pseudo = (udp_pseudo_hdr_t *)(((char *)&ip_udp->udp) - sizeof(*udp_pseudo)); payload = sendbuf + sizeof(*fh_p) + sizeof(*ip_udp); frame_length = sizeof(*fh_p) + sizeof(*ip_udp) + len; break; } } /* copy the data */ bcopy(data, payload, len); /* fill in udp pseudo header */ udp_pseudo->src_ip = src_ip; udp_pseudo->dest_ip = dest_ip; udp_pseudo->zero = 0; udp_pseudo->proto = IPPROTO_UDP; udp_pseudo->length = htons(sizeof(ip_udp->udp) + len); /* fill in UDP header */ ip_udp->udp.uh_sport = htons(src_port); ip_udp->udp.uh_dport = htons(dest_port); ip_udp->udp.uh_ulen = htons(sizeof(ip_udp->udp) + len); ip_udp->udp.uh_sum = 0; ip_udp->udp.uh_sum = in_cksum(udp_pseudo, sizeof(*udp_pseudo) + sizeof(ip_udp->udp) + len); /* fill in IP header */ bzero(ip_udp, sizeof(ip_udp->ip)); ip_udp->ip.ip_v = IPVERSION; ip_udp->ip.ip_hl = sizeof(struct ip) >> 2; ip_udp->ip.ip_ttl = MAXTTL; ip_udp->ip.ip_p = IPPROTO_UDP; ip_udp->ip.ip_src = src_ip; ip_udp->ip.ip_dst = dest_ip; ip_udp->ip.ip_len = htons(sizeof(*ip_udp) + len); ip_udp->ip.ip_id = htons(ip_id++); /* compute the IP checksum */ ip_udp->ip.ip_sum = 0; /* needs to be zero for checksum */ ip_udp->ip.ip_sum = in_cksum(&ip_udp->ip, sizeof(ip_udp->ip)); status = bpf_write(bpf_fd, sendbuf, frame_length); if (status < 0) { my_log(LOG_ERR, "bootp_session_transmit: bpf_write(%s) failed: %s (%d)", if_name, strerror(errno), errno); } } } else if (sockfd >= 0) { /* send using socket */ struct sockaddr_in dst; ssize_t send_status; bzero(&dst, sizeof(dst)); dst.sin_len = sizeof(struct sockaddr_in); dst.sin_family = AF_INET; dst.sin_port = htons(dest_port); dst.sin_addr = dest_ip; send_status = sendto(sockfd, data, len, 0, (struct sockaddr *)&dst, sizeof(struct sockaddr_in)); if (send_status < len) status = -1; } else { my_log(LOG_ERR, "bootp_session_transmit: neither bpf nor socket send available"); } if (bpf_fd >= 0) { bpf_dispose(bpf_fd); } return (status); }