apps/ipsetd-R1_2_4/ipsetd.c

Go to the documentation of this file.
00001 /*
00002  * ipsetd.c -- Daemon for setting IP address with ARP+Ping in user-space
00003  *
00004  * Copyright (C) 2001  Axis Communications AB
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00019  *
00020  * $Id: ipsetd.c,v 1.3 2008/06/27 23:50:57 spectr_rain Exp $
00021  */
00022 
00023 /*
00024  * Short description:
00025  * -c          Change IP address immediately when IP-set ping arrives.
00026  *
00027  * -e CMDLINE  Execute CMDLINE when IP-set ping arrives, the new IP address
00028  *             is available from the environment variable $NEW_IPADDR.
00029  *
00030  * -l BYTES    Specifies valid length of ICMP payload.
00031  *             ICMP packets with a different payload will not affect the
00032  *             the IP adress setting.
00033  *
00034  * -t SECONDS  Specifies how long it will take until the ipsetd exits.
00035  *
00036  * -n          Do not become a daemon.
00037  */
00038 
00039 #include <stdio.h>
00040 #include <syslog.h>
00041 #include <getopt.h>
00042 #include <stdlib.h>
00043 #include <string.h>
00044 #include <signal.h>
00045 #include <errno.h>
00046 
00047 #include <unistd.h>
00048 #include <sys/types.h>
00049 #include <sys/wait.h>
00050 #include <sys/socket.h>
00051 #include <sys/ioctl.h>
00052 #include <netinet/in.h>
00053 #include <arpa/inet.h>
00054 
00055 #include <linux/types.h>
00056 #include <linux/filter.h>
00057 #include <linux/if_ether.h>
00058 #include <linux/sockios.h>
00059 #include <net/if.h>
00060 #include <netinet/ip.h>
00061 #include <netinet/ip_icmp.h>
00062 
00063 #ifndef __UCLIBC__
00064 #include <netpacket/packet.h>
00065 #else
00066 #include <linux/if_packet.h>
00067 #endif
00068 
00069 #ifndef min
00070 #define min(a, b) (a < b ? a : b)
00071 #endif
00072 
00073 /*
00074  * The reported received ICMP packet data size includes the IP header
00075  * (normally 20 bytes) and ICMP header (8 bytes).
00076  */
00077 #define IP_ICMP_HEADERS_SIZE     (20 + 8)
00078 #define MIN_ICMP_PAYLOAD_SIZE    (ICMP_MINLEN + 4)
00079 #define MAX_ICMP_PAYLOAD_SIZE    (ETH_DATA_LEN - IP_ICMP_HEADERS_SIZE)
00080 #define IGNORE_PING_PAYLOAD_SIZE -1
00081 
00082 #define IFACE_DEFAULT         "eth0"
00083 
00084 static int ping_timeout = -1; /* never timeout */
00085 
00086 void usage(FILE *where, char *pgm)
00087 {
00088         fprintf(where,
00089                 "Usage: %s [-c] [-e command] [-t secs] "
00090                 "[-l bytes] [-i interface] [-n] [-h]\n",
00091                 pgm);
00092 }
00093 
00094 void ipsetd_timeout(int arg)
00095 {
00096         syslog(LOG_INFO, "Timed out after %ds.", ping_timeout);
00097         exit(EXIT_SUCCESS);
00098 }
00099 
00100 void set_ip(int sockfd, unsigned int ipaddr, char *ifr_name_str)
00101 {
00102         int r;
00103 
00104         struct sockaddr_in sa;
00105         struct ifreq ifr;
00106 
00107         sa.sin_family = AF_INET;
00108         sa.sin_port = 0;
00109         sa.sin_addr.s_addr = ipaddr;
00110 
00111         memset(&ifr, 0, sizeof(struct ifreq));
00112         strncpy(ifr.ifr_name, ifr_name_str, IFNAMSIZ);
00113         ifr.ifr_name[IFNAMSIZ - 1] = '\0';
00114         memcpy((char *) &ifr.ifr_addr,
00115                (char *) &sa, sizeof(struct sockaddr));
00116 
00117         ifr.ifr_addr.sa_family = AF_INET;
00118 
00119         r = ioctl(sockfd, SIOCSIFADDR, &ifr);
00120         if (r < 0) {
00121                 perror("set_ip: ioctl");
00122                 syslog(LOG_ERR, "ioctl: %s.", sys_errlist[errno]);
00123                 exit(EXIT_FAILURE);
00124         }
00125 }
00126 
00127 int is_local_ip(int sockfd, in_addr_t new_addr)
00128 {
00129         struct ifconf ifc;
00130         struct ifreq ifreqs[10];
00131         int i;
00132 
00133         memset(ifreqs, 0, sizeof ifreqs);
00134         ifc.ifc_len = sizeof ifreqs;
00135         ifc.ifc_req = ifreqs;
00136         if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
00137                 perror("is_local_ip: ioctl");
00138                 syslog(LOG_ERR, "ioctl: %m");
00139                 return 0;
00140         }
00141 
00142         for (i = 0; i < sizeof ifreqs / sizeof ifreqs[0]; i++) {
00143                 if (ifreqs[i].ifr_name[0] == '\0') {
00144                         return 0;
00145                 }
00146                 if (((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr.s_addr
00147                     == new_addr) {
00148                         return 1;
00149                 }
00150         }
00151 
00152         syslog(LOG_WARNING, "ifreqs too small to determine if address is local,"
00153                             " assuming it's not local!");
00154         return 0;
00155 }
00156 
00157 int main(int argc, char **argv)
00158 {
00159         int sockfd;
00160         int r;
00161         int c;
00162         int ping_length = IGNORE_PING_PAYLOAD_SIZE;
00163         int change_ip_on_ping = 0;
00164         char newip_exec[256] = "";
00165         char iface[IFNAMSIZ] = IFACE_DEFAULT;
00166 
00167         struct ifreq ifr;
00168         struct sockaddr_ll sl;
00169         int bind_to_interface = 0;
00170         int no_daemon = 0;
00171 
00172 /*
00173         struct sock_fprog filter_prog;
00174         struct sock_filter filter_instr[] = {
00175                 // Is ICMP ?
00176                 BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23),
00177                 BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_ICMP, 0, 3),
00178                 // Is Echo Request ?
00179 //              BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 34),
00180 //              BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ICMP_ECHO << 8, 0, 1),
00181                 // Return packet on hit
00182                 BPF_STMT(BPF_RET+BPF_K, (unsigned int)-1),
00183                 // Return no packet on miss
00184                 BPF_STMT(BPF_RET+BPF_K, 0),
00185         };
00186 */
00187 
00188         openlog("ipsetd", LOG_PID, LOG_USER);
00189 
00190         while (1) {
00191                 c = getopt(argc, argv, "t:e:l:ci:nh");
00192 
00193                 if (c == -1) {
00194                         /* End of option list */
00195                         break;
00196                 }
00197 
00198                 switch (c) {
00199                 case 't':
00200                         /* timeout */
00201                         if (optarg) {
00202                                 ping_timeout = atoi(optarg);
00203                                 if (ping_timeout < 1)
00204                                 {
00205                                         fprintf(stderr,
00206                                                 "Invalid timeout argument.\n");
00207                                         exit(EXIT_FAILURE);
00208                                 }
00209                         }
00210                         break;
00211 
00212                 case 'e':
00213                         /* exec */
00214                         if (optarg) {
00215                                 strncpy(newip_exec, optarg,
00216                                         min(strlen(optarg), sizeof(newip_exec)));
00217                                 newip_exec[sizeof(newip_exec) - 1] = '\0';
00218                         }
00219                         break;
00220 
00221                 case 'l':
00222                         /* length */
00223                         if (optarg) {
00224                                 ping_length = atoi(optarg);
00225                                 if ((ping_length < MIN_ICMP_PAYLOAD_SIZE) ||
00226                                     (ping_length > MAX_ICMP_PAYLOAD_SIZE)) {
00227                                         fprintf(stderr,
00228                                                "Invalid echo request payload "
00229                                                 "size %d (min %d, max %d).\n",
00230                                                 ping_length,
00231                                                 MIN_ICMP_PAYLOAD_SIZE,
00232                                                 MAX_ICMP_PAYLOAD_SIZE);
00233                                         exit(EXIT_FAILURE);
00234                                 }
00235                         }
00236                         break;
00237 
00238                 case 'c':
00239                         /* Change the IP on ping */
00240                         change_ip_on_ping = 1;
00241                         break;
00242 
00243                 case 'i':
00244                         bind_to_interface = 1;
00245                         if (optarg) {
00246                                 strncpy(iface, optarg, IFNAMSIZ);
00247                                 iface[IFNAMSIZ - 1] = '\0';
00248                         }
00249                         break;
00250 
00251                 case 'n':
00252                         no_daemon = 1;
00253                         break;
00254 
00255                 case 'h':
00256                         usage(stdout, argv[0]);
00257                         exit(EXIT_SUCCESS);
00258 
00259                 default:
00260                         usage(stderr, argv[0]);
00261                         exit(EXIT_FAILURE);
00262                 }
00263         }
00264 
00265 /*
00266         filter_prog.len = sizeof(filter_instr) / sizeof(struct sock_filter);
00267         filter_prog.filter = filter_instr;
00268 */
00269 
00270         sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
00271 
00272         if (sockfd < 0) {
00273                 perror("socket");
00274                 fprintf(stderr, "socket: %s.\n", sys_errlist[errno]);
00275                 exit(EXIT_FAILURE);
00276         }
00277 
00278         if (bind_to_interface) {
00279                 memset(&ifr, 0, sizeof(ifr));
00280                 strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
00281                 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
00282                 if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
00283                         perror("SIOCGIFINDEX");
00284                         fprintf(stderr, "SIOCGIFINDEX: %s.\n",
00285                                 sys_errlist[errno]);
00286                         exit(EXIT_FAILURE);
00287                 }
00288 
00289                 memset(&sl, 0, sizeof(sl));
00290                 sl.sll_family = AF_PACKET;
00291                 sl.sll_protocol = htonl(ETH_P_IP);
00292                 sl.sll_ifindex = ifr.ifr_ifindex;
00293                 if (bind(sockfd, (struct sockaddr *)&sl, sizeof(sl))) {
00294                         perror("bind");
00295                         fprintf(stderr, "bind: %s.\n", sys_errlist[errno]);
00296                         exit(EXIT_FAILURE);
00297                 }
00298         }
00299 
00300 /*
00301         r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER,
00302                        &filter_prog, sizeof(struct sock_fprog));
00303         if (r < 0) {
00304                 perror("setsockopt");
00305                 fprintf(stderr, "setsockopt: %s.\n", sys_errlist[errno]);
00306                 exit(EXIT_FAILURE);
00307         }
00308 */
00309         if (!no_daemon && (daemon(0, 0) < 0)) {
00310                 perror("daemon");
00311                 exit(EXIT_FAILURE);
00312         }
00313 
00314         syslog(LOG_INFO, "Timeout in %d s.", ping_timeout);
00315         syslog(LOG_INFO, "Expecting echo request payload %d bytes.",
00316                ping_length);
00317         syslog(LOG_INFO, "On echo request arrival, execute %s.", newip_exec);
00318 
00319         if (ping_timeout > 0) {
00320                 signal(SIGALRM, ipsetd_timeout);
00321                 alarm(ping_timeout);
00322         }
00323 
00324 //syslog(LOG_INFO, "enter while(), sockfd == %d\n", sockfd);
00325         while (1) {
00326                 struct iphdr iph;
00327                 struct icmphdr ih;
00328                 struct in_addr new_ip;
00329                 struct sockaddr_ll from;
00330                 socklen_t fromlen;
00331                 int ping_packet_len;
00332                 unsigned char buf[2000];
00333 
00334                 fromlen = sizeof(from);
00335                 r = recvfrom(sockfd, buf, sizeof(buf), 0,
00336                              (struct sockaddr *)&from, &fromlen);
00337 //syslog(LOG_INFO, "some packet... 1\n");
00338                 if (r < 0 || from.sll_pkttype != PACKET_HOST) {
00339                         continue;
00340                 }
00341 //syslog(LOG_INFO, "some packet... 2\n");
00342                 memcpy(&iph, buf + sizeof(struct ethhdr), sizeof(struct iphdr));
00343 //syslog(LOG_INFO, "some packet... 3\n");
00344                 memcpy(&ih, buf + sizeof(struct ethhdr) + sizeof(struct iphdr), sizeof(struct icmphdr));
00345 //syslog(LOG_INFO, "protocol: %d\n", iph.protocol);
00346                 if(iph.protocol != 0x01) // ICMP
00347                         continue;
00348 //syslog(LOG_INFO, "type == %d; ICMP_ECHO == %d\n", ih.type, ICMP_ECHO);
00349                 if(ih.type != ICMP_ECHO) // ICMP_ECHO request
00350                         continue;
00351                 new_ip.s_addr = iph.daddr;
00352                 ping_packet_len = ntohs(iph.tot_len) - IP_ICMP_HEADERS_SIZE;
00353 
00354                 if (((ping_length == IGNORE_PING_PAYLOAD_SIZE) ||
00355                      (ping_length == ping_packet_len)) &&
00356                     !is_local_ip(sockfd, new_ip.s_addr) &&
00357                     !is_local_ip(sockfd, iph.saddr)) {
00358                         if (change_ip_on_ping) {
00359                                 syslog(LOG_INFO, "Changing IP address "
00360                                        "on interface %s to %s.", iface,
00361                                        inet_ntoa(new_ip));
00362                                 set_ip(sockfd, new_ip.s_addr, iface);
00363                         }
00364 
00365                         if (newip_exec[0]) {
00366                                 char buffer[10];
00367 
00368                                 /* Cancel timeout */
00369                                 alarm(0);
00370                                 setenv("NEW_IPADDR", inet_ntoa(new_ip), 1);
00371                                 snprintf(buffer, sizeof(buffer), "%lu",
00372                                          (unsigned long)(ntohl(new_ip.s_addr) >> 24));
00373                                 setenv("NEW_IP_ANET", buffer, 1);
00374                                 setenv("IFNAME", iface, 1);
00375 
00376                                 r = system(newip_exec);
00377                                 if (r == -1) {
00378                                         /* Fork failed */
00379                                         syslog(LOG_ERR, "%s execution failed.",
00380                                                newip_exec);
00381                                         exit(EXIT_FAILURE);
00382                                 }
00383                                 if (WIFEXITED(r) != 0) {
00384                                         /* Child exited normally */
00385                                         syslog(LOG_INFO, "%s exit with %d.",
00386                                                newip_exec, WEXITSTATUS(r));
00387                                         exit(EXIT_SUCCESS);
00388                                 }
00389                                 if (WIFSIGNALED(r)) {
00390                                         /* Child exit with signal */
00391                                         syslog(LOG_ERR,
00392                                                "%s died with signal %d.",
00393                                                newip_exec, WTERMSIG(r));
00394                                         exit(EXIT_FAILURE);
00395                                 }
00396                         }
00397 
00398                         return 0;
00399                 }
00400         }
00401 
00402         return 0;
00403 }

Generated on Thu Aug 7 16:18:59 2008 for elphel by  doxygen 1.5.1