dhcp_prot.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,494 行 · 第 1/4 页
C
1,494 行
}
addrp = (struct sockaddr_in *) &ifrp->ifr_addr;
memset(addrp, 0, sizeof(*addrp));
addrp->sin_family = AF_INET;
addrp->sin_len = sizeof(*addrp);
addrp->sin_port = 0;
addrp->sin_addr.s_addr = INADDR_ANY;
strcpy(ifrp->ifr_name, intf);
if (ioctl(s, SIOCSIFADDR, ifrp)) { /* set ifnet address */
perror("SIOCSIFADDR");
goto out;
}
if (ioctl(s, SIOCSIFNETMASK, ifrp)) { /* set net addr mask */
perror("SIOCSIFNETMASK");
goto out;
}
/* the broadcast address is 255.255.255.255 */
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
if (ioctl(s, SIOCSIFBRDADDR, ifrp)) { /* set broadcast addr */
perror("SIOCSIFBRDADDR");
goto out;
}
ifrp->ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING;
if (ioctl(s, SIOCSIFFLAGS, ifrp)) { /* set ifnet flags */
perror("SIOCSIFFLAGS up");
goto out;
}
if (ioctl(s, SIOCGIFHWADDR, ifrp) < 0) { /* get MAC address */
perror("SIOCGIFHWADDR 1");
goto out;
}
// Set up routing
addrp->sin_family = AF_INET;
addrp->sin_port = 0;
addrp->sin_len = sizeof(*addrp); // Size of address
/* the broadcast address is 255.255.255.255 */
memset(&addrp->sin_addr, 255, sizeof(addrp->sin_addr));
memset(&route, 0, sizeof(route));
memcpy(&route.rt_gateway, addrp, sizeof(*addrp));
addrp->sin_addr.s_addr = INADDR_ANY;
memcpy(&route.rt_dst, addrp, sizeof(*addrp));
memcpy(&route.rt_genmask, addrp, sizeof(*addrp));
route.rt_dev = ifrp->ifr_name;
route.rt_flags = RTF_UP|RTF_GATEWAY;
route.rt_metric = 0;
if (ioctl(s, SIOCADDRT, &route)) { /* add route */
if (errno != EEXIST) {
perror("SIOCADDRT 3");
goto out;
}
}
retcode = true;
out:
if (s != -1)
close(s);
return retcode;
}
// ------------------------------------------------------------------------
// DHCP retransmission timeouts and number of tries
//
// To work better with simulated failures (or real ones!) so that the rest
// of the system is tested, rather than DHCP renewal failures pulling
// everything down, we try a little more zealously than the RFC suggests.
static unsigned char timeout_random = 0;
struct timeout_state {
unsigned int secs;
int countdown;
};
static inline void reset_timeout( struct timeval *ptv, struct timeout_state *pstate )
{
timeout_random++;
pstate->countdown = 4; // initial fast retries
pstate->secs = 3 + (timeout_random & 3);
ptv->tv_sec = 0;
ptv->tv_usec = 65536 * (2 + (timeout_random & 3)); // 0.1 - 0.3S, about
}
static inline int next_timeout( struct timeval *ptv, struct timeout_state *pstate )
{
if ( 0 < pstate->countdown-- )
return true;
if ( 0 == ptv->tv_sec )
ptv->tv_sec = pstate->secs;
else {
timeout_random++;
pstate->secs = ptv->tv_sec * 2 - 2 + (timeout_random & 3);
pstate->countdown = 2; // later fast retries
ptv->tv_sec = 0;
}
// If longer, too many tries...
return pstate->secs < CYGNUM_NET_DHCP_MIN_RETRY_TIME;
}
// ------------------------------------------------------------------------
// Lease expiry and alarms to notify it
static cyg_alarm_t alarm_function;
static void alarm_function(cyg_handle_t alarm, cyg_addrword_t data)
{
struct dhcp_lease *lease = (struct dhcp_lease *)data;
lease->which |= lease->next;
if ( lease->needs_attention )
cyg_semaphore_post( lease->needs_attention );
// Step the lease on into its next state of being alarmed ;-)
if ( lease->next & DHCP_LEASE_EX ) {
cyg_alarm_disable( alarm );
}
else if ( lease->next & DHCP_LEASE_T2 ) {
lease->next = DHCP_LEASE_EX;
cyg_alarm_initialize( lease->alarm, lease->expiry, 0 );
cyg_alarm_enable( lease->alarm );
}
else if ( lease->next & DHCP_LEASE_T1 ) {
lease->next = DHCP_LEASE_T2;
cyg_alarm_initialize( lease->alarm, lease->t2, 0 );
cyg_alarm_enable( lease->alarm );
}
}
static inline void no_lease( struct dhcp_lease *lease )
{
if ( lease->alarm ) {
// Already set: delete this.
cyg_alarm_disable( lease->alarm );
cyg_alarm_delete( lease->alarm );
lease->alarm = 0;
}
}
static inline void new_lease( struct bootp *bootp, struct dhcp_lease *lease )
{
cyg_tick_count_t now = cyg_current_time();
cyg_tick_count_t then;
cyg_uint32 tag = 0;
cyg_uint32 expiry_then;
cyg_resolution_t resolution =
cyg_clock_get_resolution(cyg_real_time_clock());
cyg_handle_t h;
unsigned int length;
// Silence any jabbering from past lease on this interface
no_lease( lease );
lease->which = lease->next = 0;
cyg_clock_to_counter(cyg_real_time_clock(), &h);
cyg_alarm_create( h, alarm_function, (cyg_addrword_t)lease,
&lease->alarm, &lease->alarm_obj );
// extract the lease time and scale it &c to now.
length = sizeof(tag);
if(!get_bootp_option( bootp, TAG_DHCP_LEASE_TIME, &tag ,&length))
tag = 0xffffffff;
if ( 0xffffffff == tag ) {
lease->expiry = 0xffffffff;
lease->t2 = 0xffffffff;
lease->t1 = 0xffffffff;
return; // it's an infinite lease, hurrah!
}
then = (cyg_uint64)(ntohl(tag));
expiry_then = then;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->expiry = now + then;
length = sizeof(tag);
if (get_bootp_option( bootp, TAG_DHCP_REBIND_TIME, &tag, &length ))
then = (cyg_uint64)(ntohl(tag));
else
then = expiry_then - expiry_then/4;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->t2 = now + then;
length = sizeof(tag);
if (get_bootp_option( bootp, TAG_DHCP_RENEWAL_TIME, &tag, &length ))
then = (cyg_uint64)(ntohl(tag));
else
then = expiry_then/2;
then *= 1000000000; // into nS - we know there is room in a tick_count_t
then = (then / resolution.dividend) * resolution.divisor; // into system ticks
lease->t1 = now + then;
#if 0 // for testing this mechanism
lease->expiry = now + 5000; // 1000 here makes for failure in the DHCP test
lease->t2 = now + 3500;
lease->t1 = now + 2500;
#endif
#ifdef CYGDBG_NET_DHCP_CHATTER
diag_printf("new_lease:\n");
diag_printf(" expiry = %d\n",lease->expiry);
diag_printf(" t1 = %d\n",lease->t1);
diag_printf(" t2 = %d\n",lease->t2);
#endif
lease->next = DHCP_LEASE_T1;
cyg_alarm_initialize( lease->alarm, lease->t1, 0 );
cyg_alarm_enable( lease->alarm );
}
// ------------------------------------------------------------------------
// Set all the tags we want to use when sending a packet.
// This has expanded to a large, explicit set to interwork better
// with a variety of DHCP servers.
static void set_default_dhcp_tags( struct bootp *xmit )
{
// Explicitly request full set of params that are default for LINUX
// dhcp servers, but not default for others. This is rather arbitrary,
// but it preserves behaviour for people using those servers.
// Perhaps configury of this set will be needed in future?
//
// Here's the set:
static cyg_uint8 req_list[] = {
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE
CYGOPT_NET_DHCP_PARM_REQ_LIST_REPLACE ,
#else
TAG_DHCP_SERVER_ID , // DHCP server id: 10.16.19.66
TAG_DHCP_LEASE_TIME , // DHCP time 51: 60
TAG_DHCP_RENEWAL_TIME , // DHCP time 58: 30
TAG_DHCP_REBIND_TIME , // DHCP time 59: 52
TAG_SUBNET_MASK , // subnet mask: 255.255.255.0
TAG_GATEWAY , // gateway: 10.16.19.66
TAG_DOMAIN_SERVER , // domain server: 10.16.19.66
TAG_DOMAIN_NAME , // domain name: hmt10.cambridge.redhat.com
TAG_IP_BROADCAST , // IP broadcast: 10.16.19.255
#endif
#ifdef CYGNUM_NET_SNTP_UNICAST_MAXDHCP
TAG_NTP_SERVER , // NTP Server Addresses(es)
#endif
#ifdef CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL
CYGOPT_NET_DHCP_PARM_REQ_LIST_ADDITIONAL ,
#endif
};
if ( req_list[0] ) // So that one may easily turn it all off by configury
set_variable_tag( xmit, TAG_DHCP_PARM_REQ_LIST,
&req_list[0], sizeof( req_list ) );
#ifdef CYGOPT_NET_DHCP_OPTION_HOST_NAME
{
int nlen = strlen(dhcp_hostname);
if (nlen > 0)
set_variable_tag( xmit, TAG_HOST_NAME, dhcp_hostname, nlen + 1);
}
#endif
#ifdef CYGOPT_NET_DHCP_OPTION_DHCP_CLIENTID_MAC
{
cyg_uint8 id[16+1]; /* sizeof bp_chaddr[] + 1 */
id[0] = 1; /* 1-byte hardware type: 1=ethernet. */
CYG_ASSERT( xmit->bp_hlen<=(sizeof(id)-1), "HW address invalid" );
memcpy(&id[1], &xmit->bp_chaddr, xmit->bp_hlen);
set_variable_tag( xmit, TAG_DHCP_CLIENTID, id, xmit->bp_hlen+1);
}
#endif
// Explicitly specify our max message size.
set_fixed_tag( xmit, TAG_DHCP_MAX_MSGSZ, BP_MINPKTSZ, 2 );
}
// ------------------------------------------------------------------------
// the DHCP state machine - this does all the work
int
do_dhcp(const char *intf, struct bootp *res,
cyg_uint8 *pstate, struct dhcp_lease *lease)
{
struct ifreq ifr;
struct sockaddr_in cli_addr, broadcast_addr, server_addr, rx_addr;
int s = -1;
socklen_t addrlen;
int one = 1;
unsigned char mincookie[] = {99,130,83,99,255} ;
struct timeval tv;
struct timeout_state timeout_scratch;
cyg_uint8 oldstate = *pstate;
cyg_uint8 msgtype = 0, seen_bootp_reply = 0;
unsigned int length;
cyg_uint32 xid;
#define CHECK_XID() ( /* and other details */ \
received->bp_xid != xid || /* not the same transaction */ \
received->bp_htype != xmit->bp_htype || /* not the same ESA type */ \
received->bp_hlen != xmit->bp_hlen || /* not the same length */ \
bcmp( &received->bp_chaddr, &xmit->bp_chaddr, xmit->bp_hlen ) \
)
// IMPORTANT: xmit is the same as res throughout this; *received is a
// scratch buffer for reception; its contents are always copied to res
// when we are happy with them. So we always transmit from the
// existing state.
struct bootp rx_local;
struct bootp *received = &rx_local;
struct bootp *xmit = res;
struct bootp xmit2;
int xlen;
// First, get a socket on the interface in question. But Zeroth, if
// needs be, bring it to the half-up broadcast only state if needs be.
if ( DHCPSTATE_INIT == oldstate
|| DHCPSTATE_FAILED == oldstate
|| 0 == oldstate ) {
// either explicit init state or the beginning of time or retry
if ( ! bring_half_up( intf, &ifr ) )
return false;
*pstate = DHCPSTATE_INIT;
lease->which = lease->next = 0;
}
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
goto out;
}
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one))) {
perror("setsockopt");
goto out;
}
memset((char *) &cli_addr, 0, sizeof(cli_addr));
cli_addr.sin_family = AF_INET;
cli_addr.sin_len = sizeof(cli_addr);
cli_addr.sin_addr.s_addr = htonl(INADDR_ANY);
cli_addr.sin_port = htons(IPPORT_BOOTPC);
memset((char *) &broadcast_addr, 0, sizeof(broadcast_addr));
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_len = sizeof(broadcast_addr);
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
broadcast_addr.sin_port = htons(IPPORT_BOOTPS);
memset((char *) &server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_len = sizeof(server_addr);
server_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); // overwrite later
server_addr.sin_port = htons(IPPORT_BOOTPS);
if(bind(s, (struct sockaddr *) &cli_addr, sizeof(cli_addr)) < 0) {
perror("bind error");
goto out;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
perror("setsockopt SO_REUSEADDR");
goto out;
}
if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one))) {
perror("setsockopt SO_REUSEPORT");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?