/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * 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@ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pppd.h" #include "fsm.h" #include "pathnames.h" #include "acsp.h" #include "acscp.h" #include "acscp_plugin.h" #include // required for if_ppp.h #include "../../Family/if_ppp.h" #include "../vpnd/RASSchemaDefinitions.h" #include #include #include #define ACSP_TIMEOUT_VALUE 3 /* seconds */ static acsp_ext *ext_list; // list of acsp_ext structs - one for each option type //static struct acsp_plugin *plugin_list; // list of plugin channels - not currently used static bool acsp_plugin_installed = false; //To check ACSP status externally extern bool acsp_no_routes; extern bool acsp_no_domains; extern bool acsp_use_dhcp; extern bool acsp_intercept_dhcp; extern char *serverid; // defined in sys-MacOSX.c extern char *serviceid; // defined in sys-MacOSX.c static void acsp_start_plugin(acsp_ext *ext, int mtu); static void acsp_stop_plugin(acsp_ext *ext); static void acsp_output(acsp_ext *ext); static void acsp_timeout(void *arg); static void acsp_ipdata_input(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr); static void acsp_ipdata_up(int unit, u_int32_t ouraddr, u_int32_t hisaddr); static void acsp_ipdata_down(int unit); static void acsp_ipdata_timeout(void *arg); static int acsp_ipdata_print(u_char *, int, void (*) __P((void *, char *, ...)), void *); // ACSP plugin channel functions - will eventually be called thru acsp_channel static void acsp_plugin_check_options(void); static int acsp_plugin_get_type_count(void); static int acsp_plugin_init_type(acsp_ext *ext, int type_index); //------------------------------------------------------------ //------------------------------------------------------------ // ACSP Protocol Functions //------------------------------------------------------------ //------------------------------------------------------------ //------------------------------------------------------------ // acsp_init_plugins //------------------------------------------------------------ void acsp_init_plugins(void *arg, uintptr_t phase) { acsp_ext *ext; int i, option_count; ext_list = 0; remove_notifier(&phasechange, acsp_init_plugins, 0); // load each plugin here and call start - is this the right place ??? // for each plugin acsp_plugin_check_options(); // call get_type_count option_count = acsp_plugin_get_type_count(); // for each option the plugin supports - get the ext struct initialized for (i = 0; i < option_count; i++) { if ((ext = (acsp_ext*)malloc(sizeof(acsp_ext))) == 0) { error("acscp unable allocate plugin structures\n"); acscp_protent.enabled_flag = 0; return; } if (acsp_plugin_init_type(ext, i)) { error("error initializing acsp plugin type\n"); free(ext); continue; } ext->next = 0; ext->timer_state = ACSP_TIMERSTATE_STOPPED; ext->in.size = sizeof(ACSP_Input); ext->in.mtu = 0; ext->in.log_debug = 0; ext->in.log_error = 0; ext->out.size = sizeof(ACSP_Output); // add the ext struct to the list ext->next = ext_list; ext_list = ext; } // add hook for dhcp/ipdata // will use route and domain information from acsp ipdata_input_hook = acsp_ipdata_input; ipdata_up_hook = acsp_ipdata_up; ipdata_down_hook = acsp_ipdata_down; ipdata_print_hook = acsp_ipdata_print; } //------------------------------------------------------------ // acsp_start_plugin //------------------------------------------------------------ void acsp_start(int mtu) { acsp_ext *ext; // acscp is negotiated, stop dhcp acsp_ipdata_down(0); // let the plugins do their thing ext = ext_list; while (ext) { acsp_start_plugin(ext, mtu); ext = ext->next; } } //------------------------------------------------------------ // acsp_start_plugin //------------------------------------------------------------ void acsp_stop(void) { acsp_ext *ext; // stop the plugins ext = ext_list; while (ext) { acsp_stop_plugin(ext); ext = ext->next; } } //------------------------------------------------------------ // acsp_start_plugin //------------------------------------------------------------ static void acsp_start_plugin(acsp_ext *ext, int mtu) { ext->in.mtu = mtu; ext->in.notification = ACSP_NOTIFICATION_START; ext->in.data_len = 0; ext->in.data = 0; ext->out.action = ACSP_ACTION_NONE; ext->out.data_len = 0; ext->out.data = 0; ext->process(ext->context, &ext->in, &ext->out); acsp_output(ext); } //------------------------------------------------------------ // acsp_stop_plugin //------------------------------------------------------------ static void acsp_stop_plugin(acsp_ext *ext) { if (ext->timer_state != ACSP_TIMERSTATE_STOPPED) { UNTIMEOUT(acsp_timeout, ext); ext->timer_state = ACSP_TIMERSTATE_STOPPED; } ext->in.notification = ACSP_NOTIFICATION_STOP; ext->in.data_len = 0; ext->in.data = 0; ext->out.action = ACSP_ACTION_NONE; ext->out.data_len = 0; ext->out.data = 0; ext->process(ext->context, &ext->in, &ext->out); } /* Wcast-align fix - The input and output buffers used by pppd are aligned(4) * which makes them still aligned(4) after the 4 byte ppp header is removed. * These routines expect the packet buffer to have such alignment. */ //------------------------------------------------------------ // acsp_data_input //------------------------------------------------------------ void acsp_data_input(int unit, u_char *packet, int len) { acsp_ext *ext; acsp_packet *pkt = ALIGNED_CAST(acsp_packet*)packet; if (len < ACSP_HDR_SIZE) { error("ACSP packet received was too short\n"); return; // discard the packet } // find the option struct and plugin for this packet for (ext = ext_list; ext != 0; ext = ext->next) if (ext->type == pkt->type) break; if (ext == 0) { error("ACSP packet received with invalid type\n"); return; } if (ntohs(pkt->flags) & ACSP_FLAG_ACK && ext->timer_state == ACSP_TIMERSTATE_PACKET && pkt->seq == ext->last_seq) { UNTIMEOUT(acsp_timeout, ext); ext->timer_state = ACSP_TIMERSTATE_STOPPED; } ext->in.notification = ACSP_NOTIFICATION_PACKET; ext->in.data = pkt; ext->in.data_len = len; ext->out.data = 0; ext->out.data_len = 0; ext->out.action = ACSP_ACTION_NONE; ext->process(ext->context, &ext->in, &ext->out); acsp_output(ext); } //------------------------------------------------------------ // acsp_output //------------------------------------------------------------ static void acsp_output(acsp_ext *ext) { int done = 0; u_int8_t *ptr; while (!done) { done = 1; switch (ext->out.action) { case ACSP_ACTION_NONE: break; case ACSP_ACTION_SEND: case ACSP_ACTION_SEND_WITH_TIMEOUT: if (ext->out.data_len > ext->in.mtu || ext->out.data_len < ACSP_HDR_SIZE) { error("ACSP plugin for option # %d trying to send packet of invalid length\n", ext->type); ext->in.notification = ACSP_NOTIFICATION_ERROR; ext->in.data = 0; ext->in.data_len = 0; ext->out.data = 0; ext->out.data_len = 0; ext->out.action = ACSP_ACTION_NONE; done = 0; ext->process(ext->context, &ext->in, &ext->out); } else { ptr = ext->out.data; // add address, control, and proto *ptr++ = 0xff; *ptr++ = 0x03; *(ALIGNED_CAST(u_int16_t*)ptr) = htons(PPP_ACSP); ptr += 2; if (ext->out.action == ACSP_ACTION_SEND_WITH_TIMEOUT) { if (ext->timer_state != ACSP_TIMERSTATE_STOPPED) UNTIMEOUT(acsp_timeout, ext); ext->timer_state = ACSP_TIMERSTATE_PACKET; ext->last_seq = (ALIGNED_CAST(acsp_packet*)ptr)->seq; TIMEOUT(acsp_timeout, ext, ACSP_TIMEOUT_VALUE); } output(0, ext->out.data, ext->out.data_len); if (ext->free) ext->free(ext->context, &ext->out); } break; case ACSP_ACTION_INVOKE_UI: error("ACSP plugin for option # %d attempted to invoke UI - not supported\n", ext->type); ext->in.notification = ACSP_NOTIFICATION_ERROR; ext->in.data = 0; ext->in.data_len = 0; ext->out.data = 0; ext->out.data_len = 0; ext->out.action = ACSP_ACTION_NONE; done = 0; ext->process(ext->context, &ext->in, &ext->out); break; case ACSP_ACTION_SET_TIMEOUT: if (ext->out.data_len != 4) { error("ACSP plugin for option # %d attempted timeout action with invalid time value\n", ext->type); ext->in.notification = ACSP_NOTIFICATION_ERROR; ext->in.data = 0; ext->in.data_len = 0; ext->out.data = 0; ext->out.data_len = 0; ext->out.action = ACSP_ACTION_NONE; done = 0; ext->process(ext->context, &ext->in, &ext->out); } else { ext->timer_state = ACSP_TIMERSTATE_GENERAL; TIMEOUT(acsp_timeout, ext, *((int*)ext->out.data)); } break; case ACSP_ACTION_CANCEL_TIMEOUT: if (ext->timer_state != ACSP_TIMERSTATE_STOPPED) { UNTIMEOUT(acsp_timeout, ext); ext->timer_state = ACSP_TIMERSTATE_STOPPED; } break; default: error("ACSP plugin for option # %d trying to perform invalid action\n", ext->type); ext->in.notification = ACSP_NOTIFICATION_ERROR; ext->in.data = 0; ext->in.data_len = 0; ext->out.data = 0; ext->out.data_len = 0; ext->out.action = ACSP_ACTION_NONE; done = 0; ext->process(ext->context, &ext->in, &ext->out); break; } // switch } // while } //------------------------------------------------------------ // acsp_timeout //------------------------------------------------------------ static void acsp_timeout(void *arg) { acsp_ext *ext = (acsp_ext*)arg; ext->timer_state = ACSP_TIMERSTATE_STOPPED; ext->in.notification = ACSP_NOTIFICATION_TIMEOUT; ext->in.data_len = 0; ext->in.data = 0; ext->out.action = ACSP_ACTION_NONE; ext->out.data_len = 0; ext->out.data = 0; ext->process(ext->context, &ext->in, &ext->out); acsp_output(ext); } //------------------------------------------------------------ //------------------------------------------------------------ // Plugin stuff - to be moved to plugin eventually //------------------------------------------------------------ //------------------------------------------------------------ #define STR_BUFSIZE 1024 #define ACSP_PLUGIN_MAX_RETRIES 10 // // plugin state // #define PLUGIN_STATE_NONE 0 /* no state set */ #define PLUGIN_STATE_INITIAL 1 /* setup but not started - ACSP not up */ #define PLUGIN_RCVSTATE_LISTEN 2 /* listening for incomming data */ #define PLUGIN_RCVSTATE_WAITING_PKT 3 /* waiting for more data - partial data recvd */ #define PLUGIN_SENDSTATE_WAITING_ACK 4 /* waiting for ack for last packet sent */ #define PLUGIN_STATE_DONE 5 /* done sending */ // // modes to indicate sending or receiving // #define PLUGIN_MODE_NONE 0 #define PLUGIN_MODE_RCV 1 #define PLUGIN_MODE_SEND 2 #define ACSP_ROUTEFLAGS_PRIVATE 0x0001 #define ACSP_ROUTEFLAGS_PUBLIC 0x0002 // // route struct to hold 1 route for sending or // a route received to be installed // typedef struct acsp_route { struct acsp_route *next; struct in_addr address; struct in_addr mask; struct in_addr router; u_int16_t flags; int installed; } acsp_route; // structure of route data in a packet typedef struct acsp_route_data { u_int32_t address; u_int32_t mask; u_int16_t flags; u_int16_t reserved; } acsp_route_data; // // domain struct to hold 1 domain name for sending or // a domain name recieved to be installed // typedef struct acsp_domain { struct acsp_domain *next; struct in_addr server; char *name; } acsp_domain; // structure of domain data in a packet #define ACSP_DOMAIN_DATA_HDRSIZE 6 typedef struct acsp_domain_data { u_int32_t server; u_int16_t len; char name[1]; } acsp_domain_data; // // plugin context - holds all state for a particular // option type // typedef struct acsp_plugin_context { u_int8_t type; int mode; // indicates client or server side int state; u_int8_t next_seq; int ip_up; // the following is used only by the server side int retry_count; void *next_to_send; char *buf; u_int16_t buf_size; u_int16_t last_pkt_len; void *list; int config_installed; } acsp_plugin_context; typedef struct acsp_dhcp_context { int state; int retry_count; void *route; char *domain; struct in_addr netmask; int config_installed; int unit; struct in_addr ouraddr; struct in_addr hisaddr; } acsp_dhcp_context; // // globals // // lists of routes and domains read from the config file // these lists are placed in the context for the accociated // option type when the context is created and intialized static acsp_route *routes_list = 0; static acsp_domain *domains_list = 0; static struct in_addr primary_router = { 0 }; // address of primary router (before PPP connection) static u_int32_t subnet_mask = 0; static acsp_dhcp_context *dhcp_context = 0; extern CFStringRef serviceidRef; extern SCDynamicStoreRef cfgCache; extern int publish_dns_wins_entry(CFStringRef entity, CFStringRef property1, CFTypeRef ref1, CFTypeRef ref1a, CFStringRef property2, CFTypeRef ref2, CFStringRef property3, CFTypeRef ref3, int clean); extern int route_interface(int cmd, struct in_addr host, struct in_addr mask, char iftype, char *ifname, int is_host); extern int route_gateway(int cmd, struct in_addr dest, struct in_addr mask, struct in_addr gateway, int use_gway_flag); // // funtion prototypes // static void acsp_plugin_ip_up(void *arg, uintptr_t phase); static void acsp_plugin_ip_down(void *arg, uintptr_t phase); static void acsp_plugin_install_config(acsp_plugin_context *context); static void acsp_plugin_remove_config(acsp_plugin_context *context); static void acsp_plugin_read_routes(CFPropertyListRef serverRef); static void acsp_plugin_read_domains(CFPropertyListRef serverRef); static void acsp_plugin_read_domain_from_store(void); static int acsp_plugin_alloc_context(acsp_ext *ext, u_int8_t type); static void acsp_plugin_send(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out); static void acsp_plugin_resend(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out); static void acsp_plugin_send_ack(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out); static int acsp_plugin_read(acsp_plugin_context* context, ACSP_Input* acsp_in); static void acsp_plugin_clear_list(acsp_plugin_context* context); static void acsp_plugin_dispose __P((void *context)); static void acsp_plugin_process __P((void *context, ACSP_Input *acsp_in, ACSP_Output *acsp_out)); static void acsp_plugin_print_packet __P((void (*printer)(void *, char *, ...), void *arg, u_char code, char *inbuf, int insize)); static void acsp_plugin_add_routes(acsp_route *route); static void acsp_plugin_add_domains(acsp_domain *domain); static void acsp_plugin_remove_routes(acsp_route *route); //------------------------------------------------------------ // *** Plugin functions called thru Plugin Channel *** // Used to init the plugin and init the ext structs // for each option type supported //------------------------------------------------------------ //------------------------------------------------------------ // acsp_plugin_check_options // read options from the config file //------------------------------------------------------------ static void acsp_plugin_check_options(void) { SCPreferencesRef prefs; CFPropertyListRef ref; CFStringRef idRef; char sopt[32]; CFTypeRef dictRef; CFStringRef key; CFDataRef strRef; CFPropertyListRef servers_list; routes_list = 0; domains_list = 0; if (serverid == 0) { // client side if (acsp_no_routes == 0) // command line disable ? acscp_wantoptions[0].neg_routes = 1; // default setting for client if (acsp_no_domains == 0) // command line disable ? acscp_wantoptions[0].neg_domains = 1; // default setting for client // get the primary interface router address sopt[0] = 0; key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetIPv4); if (key) { if ((dictRef = SCDynamicStoreCopyValue(cfgCache, key))) { strRef = CFDictionaryGetValue(dictRef, kSCPropNetIPv4Router); if (strRef && (CFGetTypeID(strRef) == CFStringGetTypeID())) { CFStringGetCString((CFStringRef)strRef, sopt, 32, kCFStringEncodingUTF8); } CFRelease(dictRef); } CFRelease(key); } if (sopt[0]) inet_aton(sopt, &primary_router); else primary_router.s_addr = 0; } else { /* open the prefs file */ if ((prefs = SCPreferencesCreate(0, CFSTR("pppd"), kRASServerPrefsFileName)) != 0) { // get servers list from the plist if ((servers_list = SCPreferencesGetValue(prefs, kRASServers)) != 0) { if ((idRef = CFStringCreateWithCString(0, serverid , kCFStringEncodingMacRoman)) != 0) { if ((ref = CFDictionaryGetValue(servers_list, idRef)) != 0) { if (acsp_no_routes == 0) acsp_plugin_read_routes(ref); // get routes option configuration if (acsp_no_domains == 0) acsp_plugin_read_domains(ref); // get domains option configuration } CFRelease(idRef); } CFRelease(prefs); } } } } //------------------------------------------------------------ // acsp_plugin_read_routes // read routes option configuration //------------------------------------------------------------ static void acsp_plugin_read_routes(CFPropertyListRef serverRef) { CFDictionaryRef dict; CFStringRef stringRef; CFArrayRef addrArrayRef, maskArrayRef, typeArrayRef; int addrCount, maskCount, typeCount, i; char addrStr[STR_BUFSIZE], maskStr[STR_BUFSIZE]; acsp_route *route; struct in_addr outAddr, outMask; u_int32_t flags; // routes option parameters dict = CFDictionaryGetValue(serverRef, kRASEntIPv4); if (dict && CFGetTypeID(dict) == CFDictionaryGetTypeID()) { // get the route address array addrArrayRef = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteAddresses); if (addrArrayRef && CFGetTypeID(addrArrayRef) == CFArrayGetTypeID()) { addrCount = CFArrayGetCount(addrArrayRef); // get subnet mask array maskArrayRef = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteMasks); if (maskArrayRef && CFGetTypeID(maskArrayRef) == CFArrayGetTypeID()) { maskCount = CFArrayGetCount(addrArrayRef); // get route type array typeArrayRef = CFDictionaryGetValue(dict, kRASPropIPv4OfferedRouteTypes); if (typeArrayRef && CFGetTypeID(typeArrayRef) == CFArrayGetTypeID()) { typeCount = CFArrayGetCount(typeArrayRef); if (addrCount != maskCount || addrCount != typeCount) { error("ACSP plugin: while reading prefs - route address, mask, and type counts not equal\n"); return; } acscp_allowoptions[0].neg_routes = 1; // found routes - set negotiate flag // get address and mask for each route for (i = 0; i < addrCount; i++) { stringRef = CFArrayGetValueAtIndex(addrArrayRef, i); addrStr[0] = 0; CFStringGetCString(stringRef, addrStr, STR_BUFSIZE, kCFStringEncodingUTF8); // get mask stringRef = CFArrayGetValueAtIndex(maskArrayRef, i); maskStr[0] = 0; CFStringGetCString(stringRef, maskStr, STR_BUFSIZE, kCFStringEncodingUTF8); // get route type stringRef = CFArrayGetValueAtIndex(typeArrayRef, i); if (CFStringCompare(stringRef, kRASValIPv4OfferedRouteTypesPrivate, 0) == kCFCompareEqualTo) flags = ACSP_ROUTEFLAGS_PRIVATE; else if(CFStringCompare(stringRef, kRASValIPv4OfferedRouteTypesPublic, 0) == kCFCompareEqualTo) flags = ACSP_ROUTEFLAGS_PUBLIC; else { error("ACSP plugin: invalid route type specified\n"); acscp_allowoptions[0].neg_routes = 0; break; } // allocate the structure if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) { error("ACSP plugin: no memory\n"); acscp_allowoptions[0].neg_routes = 0; break; } bzero(route, sizeof(acsp_route)); // convert if (inet_aton(addrStr, &outAddr) == 0 || inet_aton(maskStr, &outMask) == 0) { error("ACSP plugin: invalid ip address or mask specified\n"); free(route); acscp_allowoptions[0].neg_routes = 0; return; } route->address = outAddr; route->mask = outMask; route->flags = flags; route->installed = 0; route->next = routes_list; routes_list = route; } // check if we really have something to send if (routes_list == 0) acscp_allowoptions[0].neg_routes = 0; } } } } } //------------------------------------------------------------ // acsp_plugin_read_domains // read the domains option configuration //------------------------------------------------------------ static void acsp_plugin_read_domains(CFPropertyListRef serverRef) { CFDictionaryRef dict; CFStringRef stringRef; CFArrayRef nameArrayRef, serverArrayRef; int nameCount, serverCount, outlen, i; char str[STR_BUFSIZE]; acsp_domain *domain; struct in_addr outAddr; // domains option parameters dict = CFDictionaryGetValue(serverRef, kRASEntDNS); if (dict && CFGetTypeID(dict) == CFDictionaryGetTypeID()) { // get the domain names aray nameArrayRef = CFDictionaryGetValue(dict, kRASPropDNSOfferedSearchDomains); if (nameArrayRef && CFGetTypeID(nameArrayRef) == CFArrayGetTypeID()) { nameCount = CFArrayGetCount(nameArrayRef); // get the search domain server addresses if present serverArrayRef = CFDictionaryGetValue(dict, kRASPropDNSOfferedSearchDomainServers); if (serverArrayRef && CFGetTypeID(serverArrayRef) == CFArrayGetTypeID()) { serverCount = CFArrayGetCount(serverArrayRef); if (serverArrayRef && (serverCount != nameCount)) { error("ACSP plugin: search domain count not equal to search domain server count\n"); return; } } else serverCount = 0; acscp_allowoptions[0].neg_domains = 1; // enable negotiation of this option if (nameCount == 0) acsp_plugin_read_domain_from_store(); else { for (i = 0; i < nameCount; i++) { stringRef = CFArrayGetValueAtIndex(nameArrayRef, i); str[0] = 0; CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8); outlen = strlen(str); if (outlen) { if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) { error("ACSP plugin: no memory\n"); acscp_allowoptions[0].neg_domains = 0; break; } if ((domain->name = malloc(outlen + 1)) == 0) { error("ACSP plugin: no memory\n"); acscp_allowoptions[0].neg_domains = 0; free(domain); break; } memcpy(domain->name, str, outlen + 1); if (serverCount) { stringRef = CFArrayGetValueAtIndex(serverArrayRef, i); str[0] = 0; CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8); if (inet_aton(str, &outAddr) == 0) { error("ACSP plugin: invalid ip address specified for DNS server\n"); free(domain); acscp_allowoptions[0].neg_domains = 0; return; } domain->server = outAddr; } else domain->server.s_addr = 0; domain->next = domains_list; domains_list = domain; } } } // check if we really have something to send if (domains_list == 0) acscp_allowoptions[0].neg_domains = 0; } } } //------------------------------------------------------------ // acsp_plugin_get_type_count // returns the number of types supported by the plugin //------------------------------------------------------------ static void acsp_plugin_read_domain_from_store(void) { SCDynamicStoreRef storeRef; CFPropertyListRef ref = 0; CFStringRef key = 0; CFStringRef stringRef; char str[STR_BUFSIZE]; int len; acsp_domain *domain; storeRef = SCDynamicStoreCreate(0, CFSTR("pppd"), 0, 0); if (storeRef) { key = SCDynamicStoreKeyCreateNetworkGlobalEntity(0, kSCDynamicStoreDomainState, kSCEntNetDNS); if (key) { ref = SCDynamicStoreCopyValue(storeRef, key); if (ref && CFGetTypeID(ref) == CFDictionaryGetTypeID()) { stringRef = CFDictionaryGetValue(ref, kSCPropNetDNSDomainName); if (stringRef && CFGetTypeID(stringRef) == CFStringGetTypeID()) { str[0] = 0; CFStringGetCString(stringRef, str, STR_BUFSIZE, kCFStringEncodingUTF8); len = strlen(str); if (len) { if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) { error("ACSP plugin: no memory\n"); } else if ((domain->name = malloc(len + 1)) == 0) { error("ACSP plugin: no memory\n"); free(domain); } else { memcpy(domain->name, str, len + 1); domain->server.s_addr = 0; domain->next = domains_list; domains_list = domain; } } } } CFRelease(key); if (ref) CFRelease(ref); } CFRelease(storeRef); } } //------------------------------------------------------------ // acsp_plugin_get_type_count // returns the number of types supported by the plugin //------------------------------------------------------------ static int acsp_plugin_get_type_count(void) { return 2; } //------------------------------------------------------------ // acsp_plugin_init_type // init the ext struct for an option type // the option being inited is determined by the index // the function get_type_count() is used to get the // number of option types the plugin supports //------------------------------------------------------------ static int acsp_plugin_init_type(acsp_ext *ext, int type_index) { if (type_index == 0) { ext->type = CI_ROUTES; if (acsp_plugin_alloc_context(ext, CI_ROUTES) != 0) return -1; ((acsp_plugin_context*)(ext->context))->list = routes_list; // save the list read from prefs into the context } else if (type_index == 1) { ext->type = CI_DOMAINS; if (acsp_plugin_alloc_context(ext, CI_DOMAINS) != 0) return -1; ((acsp_plugin_context*)(ext->context))->list = domains_list; // save the list read from prefs into the context } else return -1; // bad index // we want the routes notifier to fire first, then the domains if (ext->type == CI_DOMAINS) add_notifier_last(&ip_up_notify, acsp_plugin_ip_up, ext->context); else add_notifier(&ip_up_notify, acsp_plugin_ip_up, ext->context); add_notifier(&ip_down_notify, acsp_plugin_ip_down, ext->context); return 0; } //------------------------------------------------------------ // acsp_plugin_alloc_context // allocate a context for the specified type //------------------------------------------------------------ static int acsp_plugin_alloc_context(acsp_ext *ext, u_int8_t type) { acsp_plugin_context* context; if ((context = (acsp_plugin_context*)malloc(sizeof(acsp_plugin_context))) == 0) { error("ACSP plugin: no memory\n"); return -1; } // allocte buffer using max mtu since nothing has been negotiated yet if ((context->buf = malloc(PPP_MTU)) == 0) { error("ACSP plugin: no memory\n"); free(context); return -1; } context->buf_size = PPP_MTU; context->type = type; context->mode = PLUGIN_MODE_NONE; context->state = PLUGIN_STATE_INITIAL; context->ip_up = 0; context->retry_count = 0; context->next_seq = 0; context->last_pkt_len = 0; context->list = 0; context->config_installed = 0; ext->context = context; // setup funtions pointers ext->dispose = acsp_plugin_dispose; ext->free = 0; ext->interactive_ui = 0; ext->process = acsp_plugin_process; ext->print_packet = acsp_plugin_print_packet; return 0; } //------------------------------------------------------------ // *** Plugin functions called thru the ext struct *** // Called for a particular option type //------------------------------------------------------------ //------------------------------------------------------------ // acsp_plugin_dispose // clean up //------------------------------------------------------------ static void acsp_plugin_dispose(void *context) { acsp_plugin_clear_list((acsp_plugin_context*)context); } //------------------------------------------------------------ // acsp_plugin_process // handle an event //------------------------------------------------------------ static void acsp_plugin_process(void *context, ACSP_Input *acsp_in, ACSP_Output *acsp_out) { acsp_packet *pkt = (acsp_packet*)acsp_in->data; acsp_plugin_context* theContext = (acsp_plugin_context*)context; switch (acsp_in->notification) { case ACSP_NOTIFICATION_NONE: break; case ACSP_NOTIFICATION_DATA_FROM_UI: error("ACSP plugin: unexpected notification received\n"); break; case ACSP_NOTIFICATION_START: // get negotiated values and setup modes in context if (theContext->type == CI_ROUTES) { if (acscp_gotoptions[0].neg_routes) theContext->mode = PLUGIN_MODE_RCV; else if (acscp_hisoptions[0].neg_routes) theContext->mode = PLUGIN_MODE_SEND; else theContext->mode = PLUGIN_MODE_NONE; } else { if (acscp_gotoptions[0].neg_domains) theContext->mode = PLUGIN_MODE_RCV; else if (acscp_hisoptions[0].neg_domains) theContext->mode = PLUGIN_MODE_SEND; else theContext->mode = PLUGIN_MODE_NONE; } // setup the context and send if required theContext->next_seq = 0; theContext->retry_count = 0; if (theContext->mode == PLUGIN_MODE_SEND) { acsp_plugin_send(theContext, acsp_in, acsp_out); theContext->next_seq++; theContext->state = PLUGIN_SENDSTATE_WAITING_ACK; } else if (theContext->mode == PLUGIN_MODE_RCV) theContext->state = PLUGIN_RCVSTATE_LISTEN; break; case ACSP_NOTIFICATION_STOP: switch (theContext->state) { case PLUGIN_SENDSTATE_WAITING_ACK: acsp_out->action = ACSP_ACTION_CANCEL_TIMEOUT; // fall thru case PLUGIN_STATE_DONE: if (theContext->config_installed) { theContext->config_installed = 0; if (theContext->type == CI_ROUTES) acsp_plugin_remove_config(theContext); } break; } // if client side - clear the list of received data if (theContext->mode == PLUGIN_MODE_RCV) acsp_plugin_clear_list(theContext); theContext->state = PLUGIN_STATE_INITIAL; break; case ACSP_NOTIFICATION_PACKET: switch (theContext->state) { case PLUGIN_RCVSTATE_LISTEN: if ((ntohs(pkt->flags) & ACSP_FLAG_START) == 0) { error("ACSP plugin: received first packet with no start flag\n"); break; } else theContext->state = PLUGIN_RCVSTATE_WAITING_PKT; // fall thru case PLUGIN_RCVSTATE_WAITING_PKT: // copy route data if (pkt->seq == theContext->next_seq) { if (acsp_plugin_read(theContext, acsp_in) == 0) { theContext->next_seq++; if (ntohs(pkt->flags) & ACSP_FLAG_END) { theContext->state = PLUGIN_STATE_DONE; if (theContext->ip_up) acsp_plugin_install_config(theContext); else notify(acspdhcpready_notifier, 0); } } else break; // bad packet or error - send no ack } if (ntohs(pkt->flags) & ACSP_FLAG_REQUIRE_ACK) acsp_plugin_send_ack(theContext, acsp_in, acsp_out); break; case PLUGIN_SENDSTATE_WAITING_ACK: if (ntohs(pkt->flags) & ACSP_FLAG_ACK) { // if ack - process it - otherwise drop the packet if (theContext->next_to_send) { acsp_plugin_send(theContext, acsp_in, acsp_out); theContext->next_seq++; } else { theContext->state = PLUGIN_STATE_DONE; notify(acspdhcpready_notifier, 0); } } break; } break; case ACSP_NOTIFICATION_TIMEOUT: if (theContext->state == PLUGIN_SENDSTATE_WAITING_ACK) { if (theContext->retry_count++ < ACSP_PLUGIN_MAX_RETRIES) acsp_plugin_resend(theContext, acsp_in, acsp_out); else { error("ACSP plugin: no acknowlegement from peer\n"); theContext->state = PLUGIN_STATE_DONE; notify(acspdhcpready_notifier, 0); } } else error("ACSP plugin: received unexpected timeout\n"); break; case ACSP_NOTIFICATION_ERROR: error("ACSP plugin: error notificationr received\n"); if (theContext->state == PLUGIN_SENDSTATE_WAITING_ACK) acsp_out->action = ACSP_ACTION_CANCEL_TIMEOUT; theContext->state = PLUGIN_STATE_DONE; notify(acspdhcpready_notifier, 0); break; default: break; } } //------------------------------------------------------------ // acsp_plugin_print_packet //------------------------------------------------------------ static void acsp_plugin_print_packet(void (*printer)(void *, char *, ...), void *arg, u_char code, char *inbuf, int insize) { } //------------------------------------------------------------ // acsp_plugin_send // send a data packet //------------------------------------------------------------ static void acsp_plugin_send(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out) { acsp_packet *pkt; u_int16_t space, len; int slen; acsp_route_data *route_data; acsp_domain_data domain_data; u_int8_t *outPtr; pkt = ALIGNED_CAST(acsp_packet*)(context->buf + 4); // leave space for address, control, and protocol space = MIN(context->buf_size, acsp_in->mtu) - 4; if (context->state == PLUGIN_STATE_INITIAL) { pkt->flags = htons(ACSP_FLAG_START); context->next_to_send = context->list; } else pkt->flags = 0; pkt->type = context->type; pkt->seq = context->next_seq; pkt->flags = htons(ntohs(pkt->flags) | ACSP_FLAG_REQUIRE_ACK); pkt->reserved = 0; len = ACSP_HDR_SIZE; switch (context->type) { case CI_ROUTES: route_data = ALIGNED_CAST(acsp_route_data*)pkt->data; while (context->next_to_send && space >= len + sizeof(acsp_route_data)) { route_data->address = ((acsp_route*)context->next_to_send)->address.s_addr; route_data->mask = ((acsp_route*)context->next_to_send)->mask.s_addr; route_data->flags = htons(((acsp_route*)context->next_to_send)->flags); route_data->reserved = htons(0); len += sizeof(acsp_route_data); context->next_to_send = ((acsp_route*)(context->next_to_send))->next; route_data++; } break; case CI_DOMAINS: // WCast-align fix - use memcpy for unaligned access outPtr = pkt->data; while (context->next_to_send && space >= (len + (slen = strlen(((acsp_domain*)(context->next_to_send))->name)) + 6)) { domain_data.server = ((acsp_domain*)(context->next_to_send))->server.s_addr; domain_data.len = htons(slen); memcpy(outPtr, &domain_data, ACSP_DOMAIN_DATA_HDRSIZE); memcpy(outPtr + ACSP_DOMAIN_DATA_HDRSIZE, ((acsp_domain*)(context->next_to_send))->name, slen); len += (slen + ACSP_DOMAIN_DATA_HDRSIZE); context->next_to_send = ((acsp_domain*)(context->next_to_send))->next; outPtr += (ACSP_DOMAIN_DATA_HDRSIZE + slen); } break; } if (context->next_to_send == 0) pkt->flags = htons(ntohs(pkt->flags) | ACSP_FLAG_END); acsp_out->action = ACSP_ACTION_SEND_WITH_TIMEOUT; context->last_pkt_len = len; pkt->len = htons(len); acsp_out->data_len = len + 4; acsp_out->data = context->buf; } //------------------------------------------------------------ // acsp_plugin_resend // re-send a data packet //------------------------------------------------------------ static void acsp_plugin_resend(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out) { // resent the packet; acsp_out->data = context->buf; acsp_out->data_len = context->last_pkt_len + 4; acsp_out->action = ACSP_ACTION_SEND_WITH_TIMEOUT; } //------------------------------------------------------------ // acsp_plugin_send_ack // send an ack //------------------------------------------------------------ static void acsp_plugin_send_ack(acsp_plugin_context* context, ACSP_Input* acsp_in, ACSP_Output* acsp_out) { acsp_packet *inPkt, *outPkt; inPkt = (acsp_packet*)acsp_in->data; outPkt = ALIGNED_CAST(acsp_packet*)(context->buf + 4); // leave space for address, control, and protocol outPkt->type = context->type; outPkt->seq = inPkt->seq; outPkt->flags = htons(ACSP_FLAG_ACK); outPkt->len = htons(ACSP_HDR_SIZE); outPkt->reserved = 0; acsp_out->action = ACSP_ACTION_SEND; acsp_out->data_len = ACSP_HDR_SIZE + 4; acsp_out->data = context->buf; } //------------------------------------------------------------ // acsp_plugin_read // process an incomming data packet //------------------------------------------------------------ static int acsp_plugin_read(acsp_plugin_context* context, ACSP_Input* acsp_in) { acsp_packet *pkt; acsp_route *route; acsp_domain *domain; int len, domain_len; acsp_route_data *route_data; acsp_domain_data domain_data; u_int8_t *inPtr; len = acsp_in->data_len - ACSP_HDR_SIZE; pkt = acsp_in->data; switch (context->type) { case CI_ROUTES: if (len % 4 != 0) { error("ACSP plugin: received packet with invalid len\n"); return -1; } route_data = ALIGNED_CAST(acsp_route_data*)pkt->data; while (len > 4) { if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) { error("ACSP plugin: no memory\n"); return -1; } bzero(route, sizeof(acsp_route)); route->address.s_addr = route_data->address & route_data->mask; route->mask.s_addr = route_data->mask; route->flags = ntohs(route_data->flags); route->installed = 0; route->next = (acsp_route*)context->list; context->list = route; len -= sizeof(acsp_route_data); route_data++; } break; case CI_DOMAINS: // WCast-align fix - this routine changed to use memcpy for unaligned access inPtr = pkt->data; while (len > 2) { memcpy(&domain_data, inPtr, ACSP_DOMAIN_DATA_HDRSIZE); if ((domain = (acsp_domain*)malloc(sizeof(acsp_domain))) == 0) { error("ACSP plugin: no memory\n"); return -1; } domain_len = ntohs(domain_data.len); if ((domain->name = malloc(domain_len + 1)) == 0) { error("ACSP plugin: no memory\n"); free(domain); return -1; } domain->server.s_addr = domain_data.server; memcpy(domain->name, inPtr + ACSP_DOMAIN_DATA_HDRSIZE, domain_len); *(domain->name + domain_len) = 0; // zero terminate domain->next = (acsp_domain*)context->list; context->list = domain; len -= (domain_len + ACSP_DOMAIN_DATA_HDRSIZE); inPtr += (ACSP_DOMAIN_DATA_HDRSIZE + domain_len); } break; default: return -1; break; } return 0; } //------------------------------------------------------------ // acsp_plugin_clear_list // clear the route or domain list in the context //------------------------------------------------------------ static void acsp_plugin_clear_list(acsp_plugin_context* context) { void* temp = context->list; while (temp) { if (context->type == CI_ROUTES) context->list = ((acsp_route*)temp)->next; else { free(((acsp_domain*)temp)->name); context->list = ((acsp_domain*)temp)->next; } free(temp); temp = context->list; } } //------------------------------------------------------------ // acsp_ip_up // called when ipcp comes up //------------------------------------------------------------ static void acsp_plugin_ip_up(void *arg, uintptr_t phase) { acsp_plugin_context* context = (acsp_plugin_context*)arg; context->ip_up = 1; if (context->mode == PLUGIN_MODE_RCV && context->state == PLUGIN_STATE_DONE && context->config_installed == 0) acsp_plugin_install_config(context); else notify(acspdhcpready_notifier, 0); } //------------------------------------------------------------ // acsp_ip_down // called when ipcp goes down //------------------------------------------------------------ static void acsp_plugin_ip_down(void *arg, uintptr_t phase) { acsp_plugin_context* context = (acsp_plugin_context*)arg; context->ip_up = 0; if (context->mode == PLUGIN_MODE_RCV && context->config_installed) acsp_plugin_remove_config(context); } //------------------------------------------------------------ // acsp_install_config // called do add the config for the option // specified by the context //------------------------------------------------------------ static void acsp_plugin_install_config(acsp_plugin_context *context) { if (context->type == CI_ROUTES) acsp_plugin_add_routes(context->list); else acsp_plugin_add_domains(context->list); context->config_installed = 1; acsp_plugin_installed = true; /* Signal that acsp info is ready */ notify(acspdhcpready_notifier, 0); } //------------------------------------------------------------ // acsp_remove_config // called do remove the config for the option // specified by the context //------------------------------------------------------------ static void acsp_plugin_remove_config(acsp_plugin_context *context) { // we don't remove anything - pppd will cleanup the dynamic store if (context->type == CI_ROUTES) acsp_plugin_remove_routes(context->list); acsp_plugin_installed = false; context->config_installed = 0; } //------------------------------------------------------------ // acsp_plugin_add_routes // install routes //------------------------------------------------------------ static void acsp_plugin_add_routes(acsp_route *route) { char route_str[INET_ADDRSTRLEN]; char mask_str[INET_ADDRSTRLEN]; char gateway_str[INET_ADDRSTRLEN]; struct in_addr addr; int err; if (route) { // only if routes are present sleep(1); addr.s_addr = 0; // remove current default route cifdefaultroute(0, 0, 0); cifroute(); #if 0 if (route_gateway(RTM_DELETE, addr, addr, addr, 1) == 0) error("ACSP plugin: error removing default route\n"); // add new default route on previous primary interface if (route_gateway(RTM_ADD, addr, addr, primary_router, 1) == 0) { error("ACSP plugin: error adding default route\n"); return; } #endif while (route) { route->installed = 1; if (route->flags & ACSP_ROUTEFLAGS_PRIVATE) { if (route->address.s_addr == 0) sifdefaultroute(0, 0, 0); else { //if (route->router.s_addr) // err = route_gateway(RTM_ADD, route->address, route->mask, route->router, 1); //else err = route_interface(RTM_ADD, route->address, route->mask, IFT_PPP, ifname, 0); if (err == 0) { error("ACSP plugin: error installing private net route. (%s/%s).", addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str), addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str)); route->installed = 0; } } } else if (route->flags & ACSP_ROUTEFLAGS_PUBLIC) { if (route->address.s_addr == 0) cifdefaultroute(0, 0, 0); else { if (route_gateway(RTM_ADD, route->address, route->mask, primary_router, 1) == 0) { error("ACSP plugin: error installing public net route. (%s/%s -> %s).", addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str), addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str), addr2ascii(AF_INET, &primary_router, sizeof(primary_router), gateway_str)); route->installed = 0; } } } route = route->next; } } } //------------------------------------------------------------ // acsp_plugin_remove_routes // remove routes //------------------------------------------------------------ static void acsp_plugin_remove_routes(acsp_route *route) { char route_str[INET_ADDRSTRLEN]; char mask_str[INET_ADDRSTRLEN]; char gateway_str[INET_ADDRSTRLEN]; while (route) { if (route->installed) { route->installed = 0; if (route->flags & ACSP_ROUTEFLAGS_PRIVATE) { if (route->address.s_addr == 0) cifdefaultroute(0, 0, 0); else { if (route_interface(RTM_DELETE, route->address, route->mask, IFT_PPP, ifname, 0) == 0) { error("ACSP plugin: error removing private net route. (%s/%s).", addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str), addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str)); route->installed = 1; } } } else if (route->flags & ACSP_ROUTEFLAGS_PUBLIC) { if (route->address.s_addr == 0) { // nothing to do } else { if (route_gateway(RTM_DELETE, route->address, route->mask, primary_router, 0) == 0) { error("ACSP plugin: error removing public net route. (%s/%s -> %s).", addr2ascii(AF_INET, &route->address, sizeof(route->address), route_str), addr2ascii(AF_INET, &route->mask, sizeof(route->mask), mask_str), addr2ascii(AF_INET, &primary_router, sizeof(primary_router), gateway_str)); route->installed = 1; } } } } route = route->next; } } //------------------------------------------------------------ // acsp_plugin_add_domains // install search domains //------------------------------------------------------------ static void acsp_plugin_add_domains(acsp_domain *domain) { CFStringRef str; int err, clean = 1; long order = 100000; CFNumberRef num; num = CFNumberCreate(NULL, kCFNumberLongType, &order); if (num == 0) { error("ACSP plugin: error adding domain name - could not create CFNumber\n"); return; } while (domain) { if ((str = CFStringCreateWithCString(NULL, domain->name, kCFStringEncodingUTF8))) { err = publish_dns_wins_entry(kSCEntNetDNS, kSCPropNetDNSSearchDomains, str, 0, kSCPropNetDNSSupplementalMatchDomains, str, kSCPropNetDNSSupplementalMatchOrders, num, clean); #ifndef kSCPropNetProxiesSupplementalMatchDomains #define kSCPropNetProxiesSupplementalMatchDomains kSCPropNetDNSSupplementalMatchDomains #define kSCPropNetProxiesSupplementalMatchOrders kSCPropNetDNSSupplementalMatchOrders #endif if (err) publish_dns_wins_entry(kSCEntNetProxies, kSCPropNetProxiesSupplementalMatchDomains, str, 0, kSCPropNetProxiesSupplementalMatchOrders, num, 0, 0, clean); CFRelease(str); if (err == 0) { error("ACSP plugin: error adding domain name\n"); goto end; } } else { error("ACSP plugin: error adding domain name - could not create CFString\n"); goto end; } domain = domain->next; clean = 0; } end: CFRelease(num); } #pragma - /* code to act as a DHCP client and server */ struct pseudo_udphdr { struct in_addr src_addr; /* source address */ struct in_addr dst_addr; /* source address */ u_int8_t zero; /* just zero */ u_int8_t proto; /* destination port */ u_int16_t len; /* packet len */ }; struct dhcp { u_char dp_op; /* packet opcode type */ u_char dp_htype; /* hardware addr type */ u_char dp_hlen; /* hardware addr length */ u_char dp_hops; /* gateway hops */ u_int32_t dp_xid; /* transaction ID */ u_int16_t dp_secs; /* seconds since boot began */ u_int16_t dp_flags; /* flags */ struct in_addr dp_ciaddr; /* client IP address */ struct in_addr dp_yiaddr; /* 'your' IP address */ struct in_addr dp_siaddr; /* server IP address */ struct in_addr dp_giaddr; /* gateway IP address */ u_char dp_chaddr[16]; /* client hardware address */ u_char dp_sname[64]; /* server host name */ u_char dp_file[128]; /* boot file name */ u_char dp_options[0]; /* variable-length options field */ }; struct dhcp_packet { struct ip ip; struct udphdr udp; struct dhcp dhcp; }; #define DHCP_COOKIE 0x63825363 #define DHCP_OPTION_END 255 #define DHCP_OPTION_LEASE_TIME 51 #define DHCP_OPTION_MSG_TYPE 53 #define DHCP_OPTION_HOST_NAME 12 #define DHCP_OPTION_SERVER_ID 54 #define DHCP_OPTION_PARAM_REQUEST_LIST 55 #define DHCP_OPTION_VENDOR_CLASS_ID 60 #define DHCP_OPTION_CLIENT_ID 61 #define DHCP_OPTION_MSG_TYPE_ACK 5 #define DHCP_OPTION_MSG_TYPE_INFORM 8 #define DHCP_OPTION_SUBNET_MASK 1 #define DHCP_OPTION_DNS 6 #define DHCP_OPTION_DOMAIN_NAME 15 #define DHCP_OPTION_WINS 43 #define DHCP_OPTION_NETBIOS 44 #define DHCP_OPTION_STATIC_ROUTE 249 #define DHCP_TIMEOUT_VALUE 3 /* seconds */ #define DHCP_MAX_RETRIES 4 //------------------------------------------------------------ // cksum //------------------------------------------------------------ static unsigned short cksum(unsigned char *data, int len) { long sum = 0; while (len > 1) { sum += *(ALIGNED_CAST(unsigned short *)data); data += sizeof(unsigned short); if (sum & 0x80000000) sum = (sum & 0xFFFF) + (sum >> 16); len -= 2; } if (len) sum += (unsigned short)*data; while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16); return ~sum; } //------------------------------------------------------------ // log_dhcp //------------------------------------------------------------ static void log_dhcp(u_char *pkt, int len, char *text) { #if 0 u_char *p; int i; u_int32_t cookie; struct dhcp *dp; struct dhcp_packet *packet; u_int32_t masklen, addrlen, addr, mask; char str[2048]; char str2[16]; /* log only if debug level is super verbose */ if (debug <= 1) return; packet = (struct dhcp_packet *)pkt; dp = (struct dhcp *)&packet->dhcp; dbglog("%s\n", text); dbglog(" op = %s\n", dp->dp_op == BOOTREQUEST ? "BOOTREQUEST" : dp->dp_op == BOOTREPLY ? "BOOTREPLY" : "UNKNOWN"); dbglog(" htype = %d\n", dp->dp_htype); dbglog(" hlen = %d\n", dp->dp_hlen); dbglog(" hops = %d\n", dp->dp_hops); dbglog(" xid = %d\n", dp->dp_xid); dbglog(" flags = %d\n", dp->dp_flags); dbglog(" client address = %s\n", inet_ntoa(dp->dp_ciaddr)); dbglog(" your address = %s\n", inet_ntoa(dp->dp_yiaddr)); dbglog(" server address = %s\n", inet_ntoa(dp->dp_siaddr)); dbglog(" gateway address = %s\n", inet_ntoa(dp->dp_giaddr)); dbglog(" hardware address = %X:%X:%X:%X:%X:%X\n", dp->dp_chaddr[0], dp->dp_chaddr[1], dp->dp_chaddr[2], dp->dp_chaddr[3], dp->dp_chaddr[4], dp->dp_chaddr[5]); dbglog(" hardware address = %X:%X:%X:%X:%X:%X\n", dp->dp_chaddr[6], dp->dp_chaddr[7], dp->dp_chaddr[8], dp->dp_chaddr[9], dp->dp_chaddr[10], dp->dp_chaddr[11]); dbglog(" hardware address = %X:%X:%X:%X\n", dp->dp_chaddr[12], dp->dp_chaddr[13], dp->dp_chaddr[14], dp->dp_chaddr[15]); dbglog(" server host name = %s\n", dp->dp_sname); dbglog(" boot file name = %s\n", dp->dp_file); p = dp->dp_options; cookie = ntohl(*(u_int32_t *)p); if (ntohl(*(u_int32_t *)p) != DHCP_COOKIE) { dbglog(" >>> incorrect cookie = %d.%d.%d.%d\n", cookie >> 24, cookie >> 16 & 0xFF, cookie >> 8 & 0xFF,cookie & 0xFF); return; } p+=4; if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || (*p != DHCP_OPTION_MSG_TYPE_INFORM && *p != DHCP_OPTION_MSG_TYPE_ACK)) { dbglog(" >>> incorrect message type = %d\n", *p); return; } dbglog(" dhcp option msg type = %s\n", *p == DHCP_OPTION_MSG_TYPE_INFORM ? "INFORM" : "ACK"); p++; len -= sizeof(struct dhcp_packet) + 7; while (*p != DHCP_OPTION_END && len > 0) { u_int8_t optcode, optlen; optcode = *p++; // check for pad option if (optcode == 0) { len--; continue; } optlen = *p++; len-=2; if (len == 0) { warning(">>> incorrect message option\n"); return; } str[0] = 0; switch (optcode) { case DHCP_OPTION_HOST_NAME: memcpy(str, p, optlen); str[optlen] = 0; dbglog(" dhcp option host name = %s\n", str); break; case DHCP_OPTION_VENDOR_CLASS_ID: memcpy(str, p, optlen); str[optlen] = 0; dbglog(" dhcp option vendor class id = %s\n", str); break; case DHCP_OPTION_CLIENT_ID: for (i = 0; i < optlen; i++) { snprintf(str2, sizeof(str2), "0x%x ", p[i]); strlcat(str, str2, sizeof(str)); } dbglog(" dhcp option client id = %s\n", str); break; case DHCP_OPTION_SERVER_ID: for (i = 0; i < optlen; i++) { snprintf(str2, sizeof(str2), "0x%x ", p[i]); strlcat(str, str2, sizeof(str)); } dbglog(" dhcp option server id = %s\n", str); break; case DHCP_OPTION_LEASE_TIME: dbglog(" dhcp option lease time = %d\n", ntohl(*(u_int32_t*)p)); break; case DHCP_OPTION_SUBNET_MASK: dbglog(" dhcp option subnet mask = %d.%d.%d.%d\n", p[0], p[1], p[2], p[3]); break; case DHCP_OPTION_DOMAIN_NAME: memcpy(str, p, optlen); str[optlen] = 0; dbglog(" dhcp option domain name = %s\n", str); break; case DHCP_OPTION_STATIC_ROUTE: dbglog(" dhcp option parameter static routes = \n"); i = 0; while (i < optlen) { masklen = p[i]; mask = 0xFFFFFFFF << (32 - masklen); addrlen = (masklen / 8); if (masklen % 8) addrlen++; addr = ntohl(*(u_int32_t*)(&p[i+1])) & mask; router = ntohl(*(u_int32_t*)(&p[i+1+addrlen])); i += addrlen + 1 + sizeof(in_addr_t); dbglog(" route %d.%d.%d.%d mask %d.%d.%d.%d router %d.%d.%d.%d\n", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF, (mask >> 24) & 0xFF, (mask >> 16) & 0xFF, (mask >> 8) & 0xFF, mask & 0xFF, (router >> 24) & 0xFF, (router >> 16) & 0xFF, (router >> 8) & 0xFF, router & 0xFF); } /* for (i = 0; i < optlen; i++) { // UNSAFE! Don't use sprintf. sprintf(str+strlen(str), "0x%x ", p[i]); } dbglog(" dhcp option parameter static routes = %s \n", str); */ break; case DHCP_OPTION_PARAM_REQUEST_LIST: for (i = 0; i < optlen; i++) { snprintf(str2, sizeof(str2), "0x%x ", p[i]); strlcat(str, str2, sizeof(str)); } dbglog(" dhcp option parameter request list = %s \n", str); break; default: dbglog(" dhcp option code = %d, len = %d\n", optcode, optlen); break; } p+=optlen; len-=optlen; } dbglog(" end of options\n"); #endif } //------------------------------------------------------------ // acsp_ipdata_send_packet //------------------------------------------------------------ static void acsp_ipdata_send_packet(int unit, u_char *data, int len, u_int32_t srcaddr, u_int16_t srcport, u_int32_t dstaddr, u_int16_t dstport, char *text) { u_char *outp; u_int16_t checksum; static u_int16_t dhcp_ip_id = 1; // build a udp pseudo header for checksum calculation outp = data + PPP_HDRLEN + sizeof(struct ip) - sizeof(struct pseudo_udphdr); PUTLONG(srcaddr, outp); // source address PUTLONG(dstaddr, outp); // destination address PUTCHAR(0, outp); // zero PUTCHAR(IPPROTO_UDP, outp); // protocol PUTSHORT(len - PPP_HDRLEN - sizeof(struct ip), outp); // total length (udp data + udp header) // build udp header outp = data + PPP_HDRLEN + sizeof(struct ip); PUTSHORT(srcport, outp); // source port PUTSHORT(dstport, outp); // dest port PUTSHORT(len - PPP_HDRLEN - sizeof(struct ip), outp); // total length (udp data + udp header) PUTSHORT(0, outp); // cksum checksum = cksum(data + PPP_HDRLEN + sizeof(struct ip) - sizeof(struct pseudo_udphdr), len - PPP_HDRLEN - sizeof(struct ip) + sizeof(struct pseudo_udphdr)); if (checksum == 0) checksum =0xffff; outp -= 2; // back to cksum PUTSHORT(0, outp); // cksum // build ip header outp = data + PPP_HDRLEN; PUTSHORT(0x4500, outp); // hdr len and service type PUTSHORT(len - PPP_HDRLEN, outp); // total length PUTSHORT(dhcp_ip_id++, outp); // identification PUTSHORT(0, outp); // flags and fragment offset PUTCHAR(0x40, outp); // ttl PUTCHAR(IPPROTO_UDP, outp); // protocol PUTSHORT(0, outp); // cksum PUTLONG(srcaddr, outp); // source address PUTLONG(dstaddr, outp); // destination address checksum = cksum(data + PPP_HDRLEN, sizeof(struct ip)); outp -= 10; // back to cksum PUTSHORT(ntohs(checksum), outp); // header checksum // log the packet log_dhcp(data + PPP_HDRLEN, len - PPP_HDRLEN, text); // now it's time to send it... output(unit, data, len); } //------------------------------------------------------------ // acsp_ipdata_input_server //------------------------------------------------------------ static void acsp_ipdata_input_server(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr) { struct dhcp_packet *dp; struct in_addr src; u_char *p, *outp; char str[2048]; int i, outlen, pad; int need_subnet_mask = 0, need_domain_name = 0, need_static_route = 0; struct dhcp reply; u_int32_t l; dp = ALIGNED_CAST(struct dhcp_packet *)pkt; /* basic length sanity check */ if (len < (sizeof(struct dhcp_packet) + 7)) { // dhcp packet + cookie + inform warning("DHCP packet received with incorrect length\n"); return; } log_dhcp(pkt, len, "DHCP packet received"); src.s_addr = dp->dhcp.dp_ciaddr.s_addr; p = dp->dhcp.dp_options; GETLONG(l, p); if (l != DHCP_COOKIE) { warning("DHCP packet received with incorrect cookie\n"); return; } if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || *p++ != DHCP_OPTION_MSG_TYPE_INFORM) { warning("DHCP packet received with incorrect message type\n"); return; } len -= sizeof(struct dhcp_packet) + 7; while (*p != DHCP_OPTION_END && len > 0) { u_int8_t optcode, optlen; optcode = *p++; // check for pad option if (optcode == 0) { len--; continue; } optlen = *p++; len-=2; if (optlen >= len) { warning("DHCP packet received with incorrect message option\n"); return; } str[0] = 0; switch (optcode) { case DHCP_OPTION_PARAM_REQUEST_LIST: for (i = 0; i < optlen; i++) { if ((p[i] == DHCP_OPTION_SUBNET_MASK) && subnet_mask) need_subnet_mask = 1; else if ((p[i] == DHCP_OPTION_DOMAIN_NAME) && domains_list) need_domain_name = 1; else if ((p[i] == DHCP_OPTION_STATIC_ROUTE) && routes_list) need_static_route = 1; } break; default: break; } p+=optlen; len-=optlen; } /* build reply dhcp packet */ if (need_subnet_mask || need_domain_name || need_static_route) { outp = outpacket_buf; // ppp MAKEHEADER(outp, PPP_IP); outlen = PPP_HDRLEN; // ip bzero(outp, sizeof(struct ip)); outp += sizeof(struct ip); outlen += sizeof(struct ip); // udp bzero(outp, sizeof(struct udphdr)); outp += sizeof(struct udphdr); outlen += sizeof(struct udphdr); // bootp memcpy(&reply, &dp->dhcp, sizeof(struct dhcp)); reply.dp_op = BOOTREPLY; reply.dp_htype = 1; reply.dp_secs = 0; memcpy(outp, &reply, sizeof(struct dhcp)); outlen += sizeof(struct dhcp); outp += sizeof(struct dhcp); // dhcp options PUTLONG(DHCP_COOKIE, outp); // dhcp cookie outlen += 4; PUTCHAR(DHCP_OPTION_MSG_TYPE, outp); // dhcp message type PUTCHAR(1, outp); // dhcp message type len PUTCHAR(DHCP_OPTION_MSG_TYPE_ACK, outp); // dhcp message type ack outlen += 3; PUTCHAR(DHCP_OPTION_SERVER_ID, outp); // dhcp server id PUTCHAR(4, outp); // dhcp server id len l = ntohl(ouraddr); PUTLONG(l, outp); // server id is our source address outlen += 6; if (need_subnet_mask) { PUTCHAR(DHCP_OPTION_SUBNET_MASK, outp); // dhcp subnet mask PUTCHAR(4, outp); // dhcp subnet mask len PUTLONG(subnet_mask, outp); // server mask outlen += 6; } if (need_domain_name) { int len; acsp_domain *list = domains_list; // domain are reversed in the list use last one. while (list->next) list = list->next; PUTCHAR(DHCP_OPTION_DOMAIN_NAME, outp); // dhcp domain name len = strlen(list->name); if (outlen + len + 2 >= sizeof(outpacket_buf)) { warning("Domain name too large for DHCP\n"); return; } PUTCHAR(len, outp); // domain name len memcpy(outp, list->name, len); // the domain outp += len; outlen += len + 2; } if (need_static_route) { acsp_route *list = routes_list; u_int32_t mask, addr, masklen, addrlen, totlen = 0; int opdone = 0; while (list) { if (list->flags & ACSP_ROUTEFLAGS_PRIVATE) { if (!opdone) { if (outlen + 2 >= sizeof(outpacket_buf)) { warning("No space for DHCP routes\n"); return; } PUTCHAR(DHCP_OPTION_STATIC_ROUTE, outp); // dhcp static route PUTCHAR(0, outp); // dhcp static route total len opdone = 1; } mask = ntohl(list->mask.s_addr); addr = ntohl(list->address.s_addr) & mask; for ( masklen = 32; masklen && (mask& 1) == 0; mask = mask >> 1, masklen--); addrlen = (masklen / 8); if (masklen % 8) addrlen++; if (outlen + addrlen + 1 + 4 >= sizeof(outpacket_buf)) { warning("Static routes list too large DHCP\n"); return; } PUTCHAR(masklen, outp); // route mask PUTLONG(addr, outp); // route address outp -= 4 - addrlen; // move pointer back according to addr len. l = ntohl(hisaddr); PUTLONG(l, outp); // router address totlen += addrlen + 1 + 4; } list = list->next; } if (opdone) { outp -= totlen + 1; PUTCHAR(totlen, outp); // move back to update option len outp += totlen; outlen += totlen + 2; } } PUTCHAR(DHCP_OPTION_END, outp); // end of options outlen ++; pad = outlen%4; for (i = 0; i < pad && outlen < sizeof(outpacket_buf); i++) { PUTCHAR(0, outp); // byte padding outlen ++; } // build ip/udp header and send it... acsp_ipdata_send_packet(unit, outpacket_buf, outlen, ntohl(ouraddr), IPPORT_BOOTPS, ntohl(src.s_addr), IPPORT_BOOTPC, "DHCP packet replied"); } } //------------------------------------------------------------ // acsp_ipdata_input_client //------------------------------------------------------------ static void acsp_ipdata_input_client(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr) { struct dhcp_packet *dp; struct in_addr src; u_char *p; u_int32_t masklen, addrlen, i, mask, l; char str[2048]; acsp_route *route; acsp_domain *domain_list = NULL, *domain; char *str_p, *tok, *delim; dp = ALIGNED_CAST(struct dhcp_packet *)pkt; /* basic length sanity check */ if (len < (sizeof(struct dhcp_packet) + 7)) { // dhcp packet + cookie + inform warning("DHCP packet received with incorrect length\n"); return; } log_dhcp(pkt, len, "DHCP packet received"); if (!dhcp_context) { // we didn't start DHCP or already closed it return; } src.s_addr = ntohl(dp->dhcp.dp_ciaddr.s_addr); p = dp->dhcp.dp_options; GETLONG(l, p); if (l != DHCP_COOKIE) { warning("DHCP packet received with incorrect cookie\n"); return; } if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || *p++ != DHCP_OPTION_MSG_TYPE_ACK) { warning("DHCP packet received with incorrect message type\n"); return; } len -= sizeof(struct dhcp_packet) + 7; while (*p != DHCP_OPTION_END && len > 0) { u_int8_t optcode, optlen; optcode = *p++; // check for pad option if (optcode == 0) { len--; continue; } optlen = *p++; len-=2; if (optlen >= len) { warning("DHCP packet received with incorrect message option\n"); return; } switch (optcode) { case DHCP_OPTION_SUBNET_MASK: memcpy(&mask, p, sizeof(u_int32_t)); // Wcast-align fix - memcpy for unaligned access if (mask && dhcp_context->ouraddr.s_addr == ouraddr && dhcp_context->netmask.s_addr != mask) { dhcp_context->netmask.s_addr = mask; if (!uifaddr(unit, dhcp_context->ouraddr.s_addr, dhcp_context->hisaddr.s_addr, dhcp_context->netmask.s_addr)) { notice("failed to configure dhcp option 'subnet mask' = %d.%d.%d.%d, our %x, his %x\n", p[0], p[1], p[2], p[3], ntohl(dhcp_context->ouraddr.s_addr), ntohl(dhcp_context->hisaddr.s_addr)); } } else { info("ignoring dhcp option 'subnet mask' = %d.%d.%d.%d, current addr %x, current mask %x\n", p[0], p[1], p[2], p[3], ntohl(dhcp_context->ouraddr.s_addr), ntohl(dhcp_context->netmask.s_addr)); } break; case DHCP_OPTION_DOMAIN_NAME: if (domain_list) { notice("ignoring dhcp option 'domain name', option already processed.\n"); break; } memcpy(str, p, optlen); str[optlen] = 0; str_p = str; // check if domain is tokenized by a variety of delimiters GET_SPLITDNS_DELIM(str, delim); tok = strsep(&str_p, delim); do { if (!tok || *tok != '\0') { // tok may be NULL the first time through here. if((domain = (__typeof__(domain))malloc(sizeof(*domain))) == NULL) { error("failed to allocate domain from DHCP packet\n"); break; } bzero(domain, sizeof(*domain)); domain->next = domain_list; domain_list = domain; if (!tok) { domain->name = str; break; } else { domain->name = tok; } } tok = strsep(&str_p, delim); } while (tok != NULL); break; case DHCP_OPTION_STATIC_ROUTE: i = 0; while (i < optlen) { if ((route = (acsp_route*)malloc(sizeof(acsp_route))) == 0) { error("DHCP: no memory\n"); return; } bzero(route, sizeof(acsp_route)); masklen = p[i]; route->mask.s_addr = htonl(0xFFFFFFFF << (32 - masklen)); addrlen = (masklen / 8); if (masklen % 8) addrlen++; memcpy(&route->address.s_addr, &p[i+1], sizeof(route->address.s_addr)); // Wcast-align fix - memcpy for unaligned access route->address.s_addr &= route->mask.s_addr; memcpy(&route->router.s_addr, &p[i+1+addrlen], sizeof(route->router.s_addr)); route->flags = ACSP_ROUTEFLAGS_PRIVATE; route->installed = 0; route->next = (acsp_route*)dhcp_context->route; dhcp_context->route = route; i += addrlen + 1 + sizeof(in_addr_t); } acsp_plugin_add_routes(dhcp_context->route); break; default: break; } p+=optlen; len-=optlen; } if (domain_list) { acsp_plugin_add_domains(domain_list); while (domain_list) { domain = domain_list; domain_list = domain_list->next; free(domain); } } /* dhcp is done */ UNTIMEOUT(acsp_ipdata_timeout, dhcp_context); dhcp_context->state = PLUGIN_STATE_DONE; /* Signal that dhcp info is ready */ notify(acspdhcpready_notifier, 0); } //------------------------------------------------------------ // acsp_ipdata_start_dhcp_client //------------------------------------------------------------ static void acsp_ipdata_start_dhcp_client(int unit, u_int32_t ouraddr, u_int32_t hisaddr) { u_char *outp; int i, outlen, pad; static u_int16_t dhcp_ip_client_xid = 1; u_int32_t l, clientid = 1; // ?? outp = outpacket_buf; // ppp MAKEHEADER(outp, PPP_IP); outlen = PPP_HDRLEN; // ip bzero(outp, sizeof(struct ip)); outp += sizeof(struct ip); outlen += sizeof(struct ip); // udp bzero(outp, sizeof(struct udphdr)); outp += sizeof(struct udphdr); outlen += sizeof(struct udphdr); // bootp bzero(outp, sizeof(struct dhcp)); PUTCHAR(BOOTREQUEST, outp); // dp_op PUTCHAR(8, outp); // dp_htype PUTCHAR(6, outp); // dp_hlen PUTCHAR(0, outp); // dp_hops PUTLONG(dhcp_ip_client_xid++, outp); // dp_xid PUTSHORT(0, outp); // dp_secs PUTSHORT(0, outp); // dp_flags l = ntohl(ouraddr); PUTLONG(l, outp); // dp_ciaddr PUTLONG(0, outp); // dp_yiaddr PUTLONG(0, outp); // dp_siaddr PUTLONG(0, outp); // dp_giaddr PUTLONG(clientid, outp); // dp_chaddr[0..3] PUTLONG(0, outp); // dp_chaddr[4..7] PUTLONG(0, outp); // dp_chaddr[8..11] PUTLONG(1, outp); // dp_chaddr[12..15] outp += 64; // dp_sname outp += 128; // dp_file outlen += sizeof(struct dhcp); // dhcp options PUTLONG(DHCP_COOKIE, outp); // dhcp cookie outlen += 4; PUTCHAR(DHCP_OPTION_MSG_TYPE, outp); // dhcp message type PUTCHAR(1, outp); // dhcp message type len PUTCHAR(DHCP_OPTION_MSG_TYPE_INFORM, outp); // dhcp message type inform outlen += 3; PUTCHAR(DHCP_OPTION_CLIENT_ID, outp); // dhcp client id PUTCHAR(7, outp); // dhcp client id len PUTCHAR(8, outp); // htype PUTLONG(clientid, outp); // client id PUTSHORT(0, outp); // client id end outlen += 9; PUTCHAR(DHCP_OPTION_PARAM_REQUEST_LIST, outp); // dhcp param request list PUTCHAR(6, outp); // dhcp param request list len PUTCHAR(DHCP_OPTION_DNS, outp); PUTCHAR(DHCP_OPTION_NETBIOS, outp); PUTCHAR(DHCP_OPTION_WINS, outp); PUTCHAR(DHCP_OPTION_SUBNET_MASK, outp); PUTCHAR(DHCP_OPTION_STATIC_ROUTE, outp); PUTCHAR(DHCP_OPTION_DOMAIN_NAME, outp); outlen += 8; PUTCHAR(DHCP_OPTION_END, outp); // end of options outlen ++; pad = outlen%4; for (i = 0; i < pad && outlen < sizeof(outpacket_buf); i++) { PUTCHAR(0, outp); // byte padding outlen ++; } // build ip/udp header and send it... acsp_ipdata_send_packet(unit, outpacket_buf, outlen, ntohl(ouraddr), IPPORT_BOOTPC, INADDR_BROADCAST, IPPORT_BOOTPS, "DHCP packet inform"); dhcp_context->state = PLUGIN_SENDSTATE_WAITING_ACK; TIMEOUT(acsp_ipdata_timeout, dhcp_context, DHCP_TIMEOUT_VALUE); } //------------------------------------------------------------ // acsp_ipdata_input //------------------------------------------------------------ static void acsp_ipdata_input(int unit, u_char *pkt, int len, u_int32_t ouraddr, u_int32_t hisaddr) { struct dhcp_packet *dp; dp = ALIGNED_CAST(struct dhcp_packet *)pkt; /* check if we received a DHCP broadcast from a client */ if (acsp_intercept_dhcp && ntohl(dp->ip.ip_dst.s_addr) == INADDR_BROADCAST && ntohs(dp->udp.uh_sport) == IPPORT_BOOTPC && ntohs(dp->udp.uh_dport) == IPPORT_BOOTPS) { acsp_ipdata_input_server(unit, pkt, len, ouraddr, hisaddr); return; } /* check if we received a DHCP reply from a server */ if (acsp_use_dhcp && dp->ip.ip_dst.s_addr == ouraddr && ntohs(dp->udp.uh_sport) == IPPORT_BOOTPS && ntohs(dp->udp.uh_dport) == IPPORT_BOOTPC) { acsp_ipdata_input_client(unit, pkt, len, ouraddr, hisaddr); return; } } #define DHCP_NOTIFY_STORE_ON_TIMEOUT_COUNT 2 //------------------------------------------------------------ // acsp_ipdatre_timeout //------------------------------------------------------------ static void acsp_ipdata_timeout(void *arg) { acsp_dhcp_context *context = (acsp_dhcp_context*)arg; if (context->state == PLUGIN_SENDSTATE_WAITING_ACK) { if (context->retry_count++ < DHCP_MAX_RETRIES) acsp_ipdata_start_dhcp_client(context->unit, context->ouraddr.s_addr, context->hisaddr.s_addr); else { dbglog("No DHCP server replied\n"); context->state = PLUGIN_STATE_DONE; } if (context->retry_count == DHCP_NOTIFY_STORE_ON_TIMEOUT_COUNT) { notify(acspdhcpready_notifier, 0); } } } //------------------------------------------------------------ // acsp_ipdata_up //------------------------------------------------------------ static void acsp_ipdata_up(int unit, u_int32_t ouraddr, u_int32_t hisaddr) { /* check if dhcp is enabled and acsp not running */ if (acsp_use_dhcp && (acscp_protent.state(unit) != OPENED)) { /* allocate dhcp routes context we don't need to keep a context for the domain */ if ((dhcp_context = (acsp_dhcp_context*)malloc(sizeof(acsp_dhcp_context))) == 0) { error("ACSP plugin: no memory to allocate DHCP routes context\n"); return; } bzero(dhcp_context, sizeof(acsp_dhcp_context)); dhcp_context->unit = unit; dhcp_context->ouraddr.s_addr = ouraddr; dhcp_context->hisaddr.s_addr = hisaddr; dhcp_context->state = PLUGIN_STATE_INITIAL; /* start dhcp client */ acsp_ipdata_start_dhcp_client(unit, ouraddr, hisaddr); } } //------------------------------------------------------------ // acsp_ipdata_down //------------------------------------------------------------ static void acsp_ipdata_down(int unit) { if (acsp_use_dhcp) { /* cleanup route */ if (dhcp_context) { acsp_plugin_remove_routes(dhcp_context->route); UNTIMEOUT(acsp_ipdata_timeout, dhcp_context); free(dhcp_context); dhcp_context = NULL; } } } //------------------------------------------------------------ // acsp_ipdata_print //------------------------------------------------------------ static int acsp_ipdata_print(pkt, plen, printer, arg) u_char *pkt; int plen; void (*printer) __P((void *, char *, ...)); void *arg; { u_char *p; int i, isbootp = 0, len = plen; struct dhcp *dp; struct dhcp_packet *packet; u_int32_t masklen, addrlen, addr, router, mask, l; char str[2048]; u_int32_t cookie; char str2[16]; packet = ALIGNED_CAST(struct dhcp_packet *)pkt; /* check if we received a DHCP broadcast from a client */ isbootp = (ntohs(packet->udp.uh_sport) == IPPORT_BOOTPC || ntohs(packet->udp.uh_sport) == IPPORT_BOOTPS) && (ntohs(packet->udp.uh_dport) == IPPORT_BOOTPS || ntohs(packet->udp.uh_dport) == IPPORT_BOOTPC); if (!isbootp) return 0; dp = (struct dhcp *)&packet->dhcp; printer(arg, " ", inet_ntoa(packet->ip.ip_src)); printer(arg, " ", inet_ntoa(packet->ip.ip_dst)); if (dp->dp_op != BOOTREPLY && dp->dp_op != BOOTREQUEST) { printer(arg, " "); return 0; } printer(arg, " ", dp->dp_op == BOOTREQUEST ? "Request" : "Reply"); /* if superverbose debug, perform additional decoding onm the packet */ if (debug > 1) { printer(arg, " ", dp->dp_htype); printer(arg, " ", dp->dp_hlen); printer(arg, " ", dp->dp_hops); printer(arg, " ", dp->dp_xid); printer(arg, " ", dp->dp_flags); printer(arg, " ", inet_ntoa(dp->dp_ciaddr)); printer(arg, " ", inet_ntoa(dp->dp_yiaddr)); printer(arg, " ", inet_ntoa(dp->dp_siaddr)); printer(arg, " ", inet_ntoa(dp->dp_giaddr)); p = &dp->dp_chaddr[0]; snprintf(str, sizeof(str), "%02x", p[0]); for (i = 1; i < 16; i++) { snprintf(str2, sizeof(str2), ":%02x", p[i]); strlcat(str, str2, sizeof(str)); } printer(arg, " ", str); printer(arg, " ", dp->dp_sname); printer(arg, " ", dp->dp_file); } p = dp->dp_options; GETLONG(l, p); cookie = l; if (cookie != DHCP_COOKIE) { printer(arg, " "); return 0; } if (debug > 1) printer(arg, " ", DHCP_COOKIE); if (*p++ != DHCP_OPTION_MSG_TYPE || *p++ != 1 || (*p != DHCP_OPTION_MSG_TYPE_INFORM && *p != DHCP_OPTION_MSG_TYPE_ACK)) { printer(arg, " "); return 0; } printer(arg, " ", *p == DHCP_OPTION_MSG_TYPE_INFORM ? "INFORM" : "ACK"); p++; len = plen - sizeof(struct dhcp_packet) - 7; while (*p != DHCP_OPTION_END && len > 0) { u_int8_t optcode, optlen; optcode = *p++; // check for pad option if (optcode == 0) { len--; continue; } optlen = *p++; len-=2; if (len == 0) { printer(arg, "