📄 bootpd.c
字号:
/* * * bootpd_nis: simple BOOTP server with NIS maps support * Copyright (C) 2005 <bfleisch@users.sourceforge.net> * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 * * * $Id: bootpd.c,v 1.4 2005/03/02 21:03:40 bfleisch Exp $ * */#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/ioctl.h>#include <net/if.h>#include <string.h>#include <sys/socket.h>#include <arpa/inet.h>#include <netdb.h>#include <net/if_arp.h>#include <signal.h>#include <fcntl.h>#include <sys/stat.h>#ifdef __linux__# include <netinet/ether.h>#else# include <netinet/if_ether.h># include <sys/sockio.h>#endif#ifdef _NISPLUS# include "nisplus.h"#endif#ifdef _NIS# include "nis.h"#endif#include "bootpd.h"#include "conf.h"#define BOOTPD_PORT 67#define BOOTP_REQUEST 1#define BOOTP_REPLY 2/* * define this to send RFC-1048 information in vendor field * in BOOTPREPLY packet. */ #define SEND_RFC1048_INFO 1/* * PID file, may be declared in Makefiles * */#ifndef PIDFILE# define PIDFILE "/var/run/bootpd.pid"#endif/* * global variables */ struct in_addr g_serv_addr ;struct in_addr g_netmask ;struct in_addr g_nameserv ;struct in_addr g_gw_addr ;struct in_addr g_broadcast ;int g_flags ;char* g_ifname ;char* g_logfile ;char* g_bootfile ;/* rfc1048 cookie value */static unsigned char g_rfc1048_cookie[] = {0x63,0x82,0x53,0x63};static intadd_rfc1048_field (char *buf, unsigned char opt, unsigned char optlen, char *optval, int maxlen){ if ( (optlen + 1 + 1) > maxlen) { log_msg(LOG_NOTICE, "BOOTP response packet too short (want %i bytes, only %i available) ! ", optlen+1+1, maxlen); return; } buf[0] = opt; if (optlen) { buf[1] = (unsigned char) optlen; memcpy (buf + 2, optval, optlen); } else memcpy (buf + 1, optval, optlen); return optlen ? (1 + 1 + optlen) : 1;}/* max BOOTP packet size */#define MAX_MSG_LEN 1024 static voidhandle_request (int sock){ char re_buf[MAX_MSG_LEN]; struct sockaddr_in re_from; size_t re_from_len; ssize_t msglen; struct in_addr ci_addr; host_info* hinfo; int idx; int clt_sock; int on=1; re_from_len = sizeof (struct sockaddr_in); memset (&re_from, 0, re_from_len); memset (re_buf, 0, MAX_MSG_LEN); if ((msglen=recvfrom (sock, re_buf, MAX_MSG_LEN, 0, (struct sockaddr *) &re_from, &re_from_len)) <= 0) { log_perror (LOG_NOTICE, "recv()"); return; } /* * returns if this is not a BOOTREQUEST packet */ if (re_buf[0] != BOOTP_REQUEST) return;#if 0 if ( g_flags & F_DEBUG)#endif { struct ether_addr a; memcpy ( &(a.ether_addr_octet), re_buf + 28, 6 ); log_msg(LOG_INFO, "BOOTPREQUEST packet from %s for %s", inet_ntoa(re_from.sin_addr), ether_ntoa(&a)); } memcpy(&ci_addr, re_buf + 12, sizeof (struct in_addr)); #ifdef _NISPLUS hinfo = get_host_info_nisplus(re_buf + 28);#else hinfo = get_host_info_nis(re_buf + 28);#endif if (!hinfo) return; /* no such hw address in our database */ /* * fill in the response packet */ re_buf[0] = BOOTP_REPLY; re_buf[1] = 1; /* Ethernet */ re_buf[2] = 6; /* 6 bytes for ethernet */ /* yiaddr*/ memcpy (re_buf + 16, &hinfo->addr, sizeof(struct in_addr)); /* siaddr*/ memcpy (re_buf + 20, &g_serv_addr, sizeof(struct in_addr)); /* boot image filename (i.e.:/tftpboot/pxelinux.0) */ if (g_bootfile) memcpy(re_buf+108, g_bootfile, MIN( (strlen(g_bootfile)+1), 128)); #ifdef SEND_RFC1048_INFO /* cookie: 0x63 0x82 0x53 0x63 */ memcpy (re_buf + 236, g_rfc1048_cookie, 4); idx=236 + 4; /* subnet field*/ idx += add_rfc1048_field (re_buf + idx, 1, 4, (char*) &g_netmask, MAX_MSG_LEN - idx); /* gateway field*/ if ( g_gw_addr.s_addr) idx += add_rfc1048_field (re_buf + idx, 3, 4, (char *) &g_gw_addr, MAX_MSG_LEN - idx); /* client name */ idx += add_rfc1048_field (re_buf + idx , 12, strlen(hinfo->hostname),hinfo->hostname, MAX_MSG_LEN - idx); /* DNS server */ if (g_nameserv.s_addr) idx += add_rfc1048_field (re_buf + idx, 6, 4, (char*) &g_nameserv, MAX_MSG_LEN - idx); /* end field*/ idx += add_rfc1048_field (re_buf + idx , 255, 0, NULL, MAX_MSG_LEN - idx); #endif /* SEND_RFC1048_INFO */ /* * accordng to RFC 951: * * 4. Chicken / Egg Issues * * How can the server send an IP datagram to the client, if the client * doesnt know its own IP address (yet)? Whenever a bootreply is being * sent, the transmitting machine performs the following operations: * * 1. If the client knows its own IP address ('ciaddr' field is * nonzero), then the IP can be sent 'as normal', since the client * will respond to ARPs [5]. * * 2. If the client does not yet know its IP address (ciaddr zero), * then the client cannot respond to ARPs sent by the transmitter of * the bootreply. There are two options: * * a. If the transmitter has the necessary kernel or driver hooks * to 'manually' construct an ARP address cache entry, then it can * fill in an entry using the 'chaddr' and 'yiaddr' fields. Of * course, this entry should have a timeout on it, just like any * other entry made by the normal ARP code itself. The * transmitter of the bootreply can then simply send the bootreply * to the client's IP address. UNIX (4.2 BSD) has this * capability. * * b. If the transmitter lacks these kernel hooks, it can simply * send the bootreply to the IP broadcast address on the * appropriate interface. This is only one additional broadcast * over the previous case. * * */ if ( ci_addr.s_addr) /* client knows its IP address */ { memcpy (&(re_from.sin_addr), &ci_addr, sizeof(struct in_addr)); clt_sock = sock; } else { /* * setup ARP entry * */ #ifdef SIOCSARP struct arpreq areq; int s; memset (&areq, 0, sizeof(struct arpreq)); /* * ip address */ ((struct sockaddr_in*)&areq.arp_pa)->sin_addr.s_addr = hinfo->addr.s_addr; areq.arp_pa.sa_family = AF_INET; /* * hw address */ memcpy ( &areq.arp_ha.sa_data, re_buf + 28, 6 ); areq.arp_ha.sa_family = ARPHRD_ETHER; areq.arp_flags=ATF_COM;#ifdef __linux__ strcpy(areq.arp_dev, g_ifname); #endif s = socket(AF_INET, SOCK_DGRAM, 0); if (ioctl(s,SIOCSARP, &areq) != 0) log_perror(LOG_NOTICE, "ioctl(SIOCSARP)"); close(s); clt_sock = socket(AF_INET, SOCK_DGRAM, 0); setsockopt (clt_sock,SOL_SOCKET,SO_BROADCAST, &on,4); memcpy( &(re_from.sin_addr), &hinfo->addr, sizeof (struct in_addr));#else /* SIOCSARP */ /* * run /sbin/arp to update our ARP cache */ #error "TO BE IMPLEMENTED !" #endif /* SIOCSARP */ } if (sendto (clt_sock, &re_buf, msglen, 0, (struct sockaddr *) &re_from, re_from_len) <= 0) log_perror (LOG_NOTICE, "sendto() failed:"); if ( clt_sock != sock && (clt_sock)) close(clt_sock);}static voidusage(const char* pg){printf("\n""\n""%s: BOOTP server (v%s), with %s support - \n""\n""Usage:\n""\t%s -i <inteface> [<options>] \n""\n""Required switches: \n""\t-i <interface> : interface name (eth0, hme0, ...)\n""\n""Optional switches: \n""\t-g <gateway> : gateway address (IP or hostname)\n""\t-m <netmask> : netmask address \n""\t-n <name server> : name server address (IP or hostname)\n""\t-s <server> : Our address (IP or hostname)\n""\t-b <broadcast> : network broadcast address\n""\t-l <logfile> : redirect logs to <logfile>\n""\t-B <bootfile> : boot file name\n""\t-x : run from {x,}inetd\n""\t-d : debug mode\n""\n\n\n", pg, BOOTPDNIS_RELEASE,#if defined(_NIS) "NIS" ,#elif defined(_NISPLUS) "NIS+" ,#else "" ,#endif pg); exit(0);}voidresolve_addr(const char* host, struct in_addr* addr){ struct hostent *he; memset(addr, 0, sizeof (struct in_addr)); he = gethostbyname(host); if (!he) { perror(host); return; } if ( he->h_addrtype != AF_INET) { log_msg(LOG_ERR, "%s is not an INET address\n", host); return; } memcpy(addr, he->h_addr, sizeof (struct in_addr));}intmain(int argc, char **argv){ struct sockaddr_in server_addr; char opt; int server_socket; struct if_info *if_info; memset(&g_gw_addr, 0, sizeof(struct in_addr)); memset(&g_nameserv, 0, sizeof(struct in_addr)); memset(&g_netmask, 0, sizeof(struct in_addr)); memset(&g_serv_addr, 0, sizeof(struct in_addr)); memset(&g_broadcast, 0, sizeof(struct in_addr)); g_ifname = NULL; g_logfile = NULL; g_flags =0; g_bootfile= NULL; while ( (opt=getopt(argc, argv, "hdg:i:n:m:s:b:xl:fB:c:")) != EOF) { switch (opt) { case 'c': if (conf_parse_file(optarg) == 0) break; log_msg(LOG_NOTICE, "Exiting due to previous errors."); exit(1); break; case 'g': resolve_addr(optarg, &g_gw_addr); break; case 'n': resolve_addr(optarg, &g_nameserv); break; case 'm': resolve_addr(optarg, &g_netmask); break; case 'b': resolve_addr(optarg, &g_broadcast); break; case 'i': g_ifname = strdup(optarg); break; case 's': resolve_addr(optarg, &g_serv_addr); break; case 'x': g_flags |=F_INETD; break; case 'd': g_flags |=F_DEBUG; break; case 'l': g_logfile = strdup(optarg); break; case 'f': g_flags |=F_FOREGROUND; break; case 'B': g_bootfile = strdup(optarg); break; case 'h': default: usage(argv[0]); break; } } if (g_logfile) { log_set_logfile(g_logfile); } else { if ( g_flags & F_FOREGROUND) log_set_stderr(); else log_set_syslog("bootpd_nis", LOG_PID, LOG_USER); } /* * guess if we are a child of {x,}inetd */ if ( !(g_flags & F_INETD)) { struct sockaddr saddr; socklen_t saddr_len; saddr_len = sizeof(struct sockaddr); if (getsockname(0, &saddr, &saddr_len)==0) g_flags |= F_INETD; } /* * become a daemon if not started by inetd * */ if ( ! (g_flags & F_INETD)) { int pid; int i; if (! (g_flags & F_FOREGROUND)) { switch ( (pid=fork())) { case -1: log_perror(LOG_ERR, "fork()"); exit (1); break; case 0: break; default: /* parent */ exit(0); } /* * close standard files descriptors */ //close(0); close(1); close(2); if (setsid() == (pid_t) -1) log_perror(LOG_ERR, "setsid() error:"); /* i=open("/dev/null",O_RDWR); dup2(i,0); dup2(i,1); */ umask(777); chdir("/"); signal(SIGCHLD,SIG_IGN); /* ignore child */ signal(SIGTSTP,SIG_IGN); /* ignore tty signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); } /* * test lock & run file */ #ifdef PIDFILE i = open(PIDFILE, O_WRONLY | O_CREAT, 0644); if (i< 0) { log_perror(LOG_ERR, "Could not create lock file %s:", PIDFILE); exit(1); } if (lockf (i, F_TLOCK, 0) != 0) { log_perror(LOG_ERR, "Could not lock %s. Another instance already running ? ", PIDFILE); exit(1); } { char buf[16]; snprintf(buf, 16, "%i\n", getpid()); write(i, buf, strlen(buf)); }#endif /* PIDFILE */ } if ( ! g_ifname) if ( ! (g_flags & F_INETD)) { log_msg(LOG_ERR, "Interface name is mandatory. Exiting."); exit(1); } /* * lookup interface(s) info */ if_info = get_if_info (g_ifname); if ( ! if_info) { log_msg(LOG_ERR, "%s: no such interface.\n", g_ifname); exit(1); } memcpy ( &g_serv_addr, &(if_info->if_addr), sizeof(struct in_addr)); if (! g_netmask.s_addr) memcpy ( &g_netmask, &(if_info->if_netmask), sizeof(struct in_addr)); if (! g_broadcast.s_addr) memcpy ( &g_broadcast, &(if_info->if_broadcast), sizeof(struct in_addr)); if (g_flags & F_DEBUG) { log_msg(LOG_DEBUG, "Starting %s (iface=%s, myip=%s, broadcast=%s, netmask=%s, gateway=%s)", argv[0], g_ifname, strdup (inet_ntoa (g_serv_addr)), strdup (inet_ntoa (g_broadcast)), strdup (inet_ntoa (g_netmask)), g_gw_addr.s_addr ? strdup( inet_ntoa(g_gw_addr)) : "(none)"); } if ( g_flags & F_INETD) server_socket = 0; else { /* * prepare server socket */ server_socket = socket (PF_INET, SOCK_DGRAM, 0); if (server_socket < 0) { log_perror (LOG_ERR, "socket(): "); exit (1); } memset (&server_addr, 0, sizeof (struct sockaddr_in)); server_addr.sin_port = htons (BOOTPD_PORT); server_addr.sin_addr.s_addr = g_broadcast.s_addr; if (bind (server_socket, (struct sockaddr *) &server_addr, sizeof (struct sockaddr_in)) < 0) { log_perror (LOG_ERR, "bind(): "); exit (1); } } do { handle_request (server_socket); } while (! (g_flags & F_INETD)); if (server_socket) close (server_socket); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -