Option to use with setsockopt at the IPPROTO_IP level

本文深入探讨了IP协议中各种选项的作用及其在实际网络应用中的重要性,包括选项的设置与获取方法,如类型服务和优先级、存活时间、组播接口和组播生存时间等。
/*   Option   to   use   with   setsockopt   at   the   IPPROTO_IP   level   */  

#define IP_OPTIONS 1   /*   set/get   IP   options   */  
#define IP_HDRINCL 2   /*   header   is   included   with   data   */  
#define IP_TOS 3   /*   IP   type   of   service   and   preced*/  
#define IP_TTL 4   /*   IP   time   to   live   */  
#define IP_MULTICAST_IF 9   /*   set/get   IP   multicast   i/f     */  
#define IP_MULTICAST_TTL               10   /*   set/get   IP   multicast   ttl   */ 
#define IP_MULTICAST_LOOP 11 /*set/get IP multicast loopback */ 
#define IP_ADD_MEMBERSHIP             12   /*   add   an   IP   group   membership   */ 
#define IP_DROP_MEMBERSHIP           13/*   drop   an   IP   group   membership   */ 
#define   IP_DONTFRAGMENT           14   /*   don 't   fragment   IP   datagrams   */


 

/* vi: set sw=4 ts=4: */ /* * udhcp client * * Russ Dill <Russ.Dill@asu.edu> July 2001 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <syslog.h> /* Override ENABLE_FEATURE_PIDFILE - ifupdown needs our pidfile to always exist */ #define WANT_PIDFILE 1 #include "common.h" #include "dhcpd.h" #include "dhcpc.h" #include "libmsglog.h" #include <netinet/if_ether.h> #include <linux/filter.h> #include <linux/if_packet.h> /* struct client_config_t client_config is in bb_common_bufsiz1 */ #if ENABLE_LONG_OPTS static const char udhcpc_longopts[] ALIGN1 = "clientid-none\0" No_argument "C" "vendorclass\0" Required_argument "V" "hostname\0" Required_argument "H" "fqdn\0" Required_argument "F" "interface\0" Required_argument "i" "now\0" No_argument "n" "pidfile\0" Required_argument "p" "quit\0" No_argument "q" "release\0" No_argument "R" "request\0" Required_argument "r" "script\0" Required_argument "s" "timeout\0" Required_argument "T" "version\0" No_argument "v" "retries\0" Required_argument "t" "tryagain\0" Required_argument "A" "syslog\0" No_argument "S" "request-option\0" Required_argument "O" "no-default-options\0" No_argument "o" "foreground\0" No_argument "f" "background\0" No_argument "b" "broadcast\0" No_argument "B" IF_FEATURE_UDHCPC_ARPING("arping\0" No_argument "a") IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") "keep-request" No_argument "k" ; #endif /* Must match getopt32 option string order */ enum { OPT_C = 1 << 0, OPT_V = 1 << 1, OPT_H = 1 << 2, OPT_h = 1 << 3, OPT_F = 1 << 4, OPT_i = 1 << 5, OPT_n = 1 << 6, OPT_p = 1 << 7, OPT_q = 1 << 8, OPT_R = 1 << 9, OPT_r = 1 << 10, OPT_s = 1 << 11, OPT_T = 1 << 12, OPT_t = 1 << 13, OPT_S = 1 << 14, OPT_A = 1 << 15, OPT_O = 1 << 16, OPT_o = 1 << 17, OPT_x = 1 << 18, OPT_f = 1 << 19, OPT_B = 1 << 20, /* The rest has variable bit positions, need to be clever */ OPTBIT_B = 20, USE_FOR_MMU( OPTBIT_b,) IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) IF_FEATURE_UDHCP_PORT( OPTBIT_P,) #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 OPTBIT_v, #endif OPTBIT_k, USE_FOR_MMU( OPT_b = 1 << OPTBIT_b,) IF_FEATURE_UDHCPC_ARPING(OPT_a = 1 << OPTBIT_a,) IF_FEATURE_UDHCP_PORT( OPT_P = 1 << OPTBIT_P,) #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 OPT_v = 1 << OPTBIT_v, #endif OPT_k = 1 << OPTBIT_k, }; /*** Script execution code ***/ /* get a rough idea of how long an option will be (rounding up...) */ static const uint8_t len_of_option_as_string[] = { [OPTION_IP ] = sizeof("255.255.255.255 "), [OPTION_IP_PAIR ] = sizeof("255.255.255.255 ") * 2, [OPTION_ROUTES ] = sizeof("255.255.255.255 ") * 2, [OPTION_STATIC_ROUTES ] = sizeof("255.255.255.255/32 255.255.255.255 "), [OPTION_6RD ] = sizeof("32 128 FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF 255.255.255.255 "), [OPTION_STRING ] = 1, [OPTION_STRING_HOST ] = 1, #if ENABLE_FEATURE_UDHCP_RFC3397 [OPTION_DNS_STRING ] = 1, /* unused */ /* Hmmm, this severely overestimates size if SIP_SERVERS option * is in domain name form: N-byte option in binary form * mallocs ~16*N bytes. But it is freed almost at once. */ [OPTION_SIP_SERVERS ] = sizeof("255.255.255.255 "), #endif // [OPTION_BOOLEAN ] = sizeof("yes "), [OPTION_U8 ] = sizeof("255 "), [OPTION_U16 ] = sizeof("65535 "), // [OPTION_S16 ] = sizeof("-32768 "), [OPTION_U32 ] = sizeof("4294967295 "), [OPTION_S32 ] = sizeof("-2147483684 "), }; /* note: ip is a pointer to an IP in network order, possibly misaliged */ static int sprint_nip(char *dest, const char *pre, const uint8_t *ip) { return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]); } static int sprint_nip6(char *dest, const char *pre, const uint8_t *ip) { int len = 0; int off; uint16_t word; len += sprintf(dest, "%s", pre); for (off = 0; off < 16; off += 2) { move_from_unaligned16(word, &ip[off]); len += sprintf(dest+len, "%s%04X", off ? ":" : "", htons(word)); } return len; } /* really simple implementation, just count the bits */ static int mton(uint32_t mask) { int i = 0; mask = ntohl(mask); /* 111110000-like bit pattern */ while (mask) { i++; mask <<= 1; } return i; } /* Check if a given label represents a valid DNS label * Return pointer to the first character after the label upon success, * NULL otherwise. * See RFC1035, 2.3.1 */ /* We don't need to be particularly anal. For example, allowing _, hyphen * at the end, or leading and trailing dots would be ok, since it * can't be used for attacks. (Leading hyphen can be, if someone uses * cmd "$hostname" * in the script: then hostname may be treated as an option) */ static const char *valid_domain_label(const char *label) { unsigned char ch; unsigned pos = 0; for (;;) { ch = *label; if ((ch|0x20) < 'a' || (ch|0x20) > 'z') { if (pos == 0) { /* label must begin with letter */ return NULL; } if (ch < '0' || ch > '9') { if (ch == '\0' || ch == '.') return label; /* DNS allows only '-', but we are more permissive */ if (ch != '-' && ch != '_') return NULL; } } label++; pos++; //Do we want this? //if (pos > 63) /* NS_MAXLABEL; labels must be 63 chars or less */ // return NULL; } } /* Check if a given name represents a valid DNS name */ /* See RFC1035, 2.3.1 */ static int good_hostname(const char *name) { //const char *start = name; for (;;) { name = valid_domain_label(name); if (!name) return 0; if (!name[0]) return 1; //Do we want this? //return ((name - start) < 1025); /* NS_MAXDNAME */ name++; } } /* Create "opt_name=opt_value" string */ static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name) { unsigned upper_length; int len, type, optlen; char *dest, *ret; /* option points to OPT_DATA, need to go back and get OPT_LEN */ len = option[OPT_LEN - OPT_DATA]; type = optflag->flags & OPTION_TYPE_MASK; optlen = dhcp_option_lengths[type]; upper_length = len_of_option_as_string[type] * ((unsigned)len / (unsigned)optlen); dest = ret = xmalloc(upper_length + strlen(opt_name) + 2); dest += sprintf(ret, "%s=", opt_name); while (len >= optlen) { switch (type) { case OPTION_IP_PAIR: dest += sprint_nip(dest, "", option); *dest++ = '/'; dest += sprint_nip(dest, "", option + 4); break; case OPTION_ROUTES: { unsigned masklen; uint8_t *ipaddr = option; uint8_t *gateway = option + 4; //routesʹ����Ȼ���룬����ip��λ�Ƿ�Ϊ0��ȡ8/16/24/32���� masklen = (ipaddr[0] != 0) + ((ipaddr[0] != 0) && (ipaddr[1] != 0)) + ((ipaddr[0] != 0) && (ipaddr[1] != 0) && (ipaddr[2] != 0)) + ((ipaddr[0] != 0) && (ipaddr[1] != 0) && (ipaddr[2] != 0) && (ipaddr[3] != 0)); masklen *= 8; dest += sprint_nip(dest, "", ipaddr); dest += sprintf(dest, "/%u ", masklen); dest += sprint_nip(dest, "", gateway); break; } case OPTION_IP: dest += sprint_nip(dest, "", option); break; // case OPTION_BOOLEAN: // dest += sprintf(dest, *option ? "yes" : "no"); // break; case OPTION_U8: dest += sprintf(dest, "%u", *option); break; // case OPTION_S16: case OPTION_U16: { uint16_t val_u16; move_from_unaligned16(val_u16, option); dest += sprintf(dest, "%u", ntohs(val_u16)); break; } case OPTION_S32: case OPTION_U32: { uint32_t val_u32; move_from_unaligned32(val_u32, option); dest += sprintf(dest, type == OPTION_U32 ? "%lu" : "%ld", (unsigned long) ntohl(val_u32)); break; } case OPTION_STRING: case OPTION_STRING_HOST: memcpy(dest, option, len); dest[len] = '\0'; if (type == OPTION_STRING_HOST && !good_hostname(dest)) safe_strncpy(dest, "bad", len); return ret; /* Short circuit this case */ case OPTION_STATIC_ROUTES: { /* Option binary format: * mask [one byte, 0..32] * ip [big endian, 0..4 bytes depending on mask] * router [big endian, 4 bytes] * may be repeated * * We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2" */ const char *pfx = ""; while (len >= 1 + 4) { /* mask + 0-byte ip + router */ uint32_t nip; uint8_t *p; unsigned mask; int bytes; mask = *option++; if (mask > 32) break; len--; nip = 0; p = (void*) &nip; bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */ while (--bytes >= 0) { *p++ = *option++; len--; } if (len < 4) break; /* print ip/mask */ dest += sprint_nip(dest, pfx, (void*) &nip); pfx = " "; dest += sprintf(dest, "/%u ", mask); /* print router */ dest += sprint_nip(dest, "", option); option += 4; len -= 4; } return ret; } case OPTION_6RD: { /* Option binary format: * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | OPTION_6RD | option-length | IPv4MaskLen | 6rdPrefixLen | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * | 6rdPrefix | * | (16 octets) | * | | * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 6rdBRIPv4Address(es) | * . . * . . * . . * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * We convert it to a string "IPv4MaskLen 6rdPrefixLen 6rdPrefix 6rdBRIPv4Address" */ /* Sanity check: ensure that our length is at least 22 bytes, that * IPv4MaskLen is <= 32, 6rdPrefixLen <= 128 and that the sum of * (32 - IPv4MaskLen) + 6rdPrefixLen is less than or equal to 128. * If any of these requirements is not fulfilled, return with empty * value. */ if ((len >= 22) && (*option <= 32) && (*(option+1) <= 128) && (((32 - *option) + *(option+1)) <= 128)) { /* IPv4MaskLen */ dest += sprintf(dest, "%u ", *option++); len--; /* 6rdPrefixLen */ dest += sprintf(dest, "%u ", *option++); len--; /* 6rdPrefix */ dest += sprint_nip6(dest, "", option); option += 16; len -= 16; /* 6rdBRIPv4Addresses */ while (len >= 4) { dest += sprint_nip(dest, " ", option); option += 4; len -= 4; /* the code to determine the option size fails to work with * lengths that are not a multiple of the minimum length, * adding all advertised 6rdBRIPv4Addresses here would * overflow the destination buffer, therefore skip the rest * for now */ break; } } return ret; } #if ENABLE_FEATURE_UDHCP_RFC3397 case OPTION_DNS_STRING: /* unpack option into dest; use ret for prefix (i.e., "optname=") */ dest = dname_dec(option, len, ret); if (dest) { free(ret); return dest; } /* error. return "optname=" string */ return ret; case OPTION_SIP_SERVERS: /* Option binary format: * type: byte * type=0: domain names, dns-compressed * type=1: IP addrs */ option++; len--; if (option[-1] == 0) { dest = dname_dec(option, len, ret); if (dest) { free(ret); return dest; } } else if (option[-1] == 1) { const char *pfx = ""; while (1) { len -= 4; if (len < 0) break; dest += sprint_nip(dest, pfx, option); pfx = " "; option += 4; } } return ret; #endif } /* switch */ option += optlen; len -= optlen; // TODO: it can be a list only if (optflag->flags & OPTION_LIST). // Should we bail out/warn if we see multi-ip option which is // not allowed to be such (for example, DHCP_BROADCAST)? - if (len <= 0 /* || !(optflag->flags & OPTION_LIST) */) break; *dest++ = ' '; *dest = '\0'; } return ret; } /* put all the parameters into the environment */ static char **fill_envp(struct dhcp_packet *packet) { int envc; int i; char **envp, **curr; const char *opt_name; uint8_t *temp; uint8_t overload = 0; #define BITMAP unsigned #define BBITS (sizeof(BITMAP) * 8) #define BMASK(i) (1 << (i & (sizeof(BITMAP) * 8 - 1))) #define FOUND_OPTS(i) (found_opts[(unsigned)i / BBITS]) BITMAP found_opts[256 / BBITS]; memset(found_opts, 0, sizeof(found_opts)); /* We need 6 elements for: * "interface=IFACE" * "ip=N.N.N.N" from packet->yiaddr * "siaddr=IP" from packet->siaddr_nip (unless 0) * "boot_file=FILE" from packet->file (unless overloaded) * "sname=SERVER_HOSTNAME" from packet->sname (unless overloaded) * terminating NULL */ envc = 6; /* +1 element for each option, +2 for subnet option: */ if (packet) { /* note: do not search for "pad" (0) and "end" (255) options */ //TODO: change logic to scan packet _once_ for (i = 1; i < 255; i++) { temp = udhcp_get_option(packet, i); if (temp) { if (i == DHCP_OPTION_OVERLOAD) overload = *temp; else if (i == DHCP_SUBNET) envc++; /* for mton */ envc++; /*if (i != DHCP_MESSAGE_TYPE)*/ FOUND_OPTS(i) |= BMASK(i); } } } curr = envp = xzalloc(sizeof(envp[0]) * envc); *curr = xasprintf("interface=%s", client_config.interface); putenv(*curr++); if (!packet) return envp; *curr = xmalloc(sizeof("ip=255.255.255.255")); sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr); putenv(*curr++); opt_name = dhcp_option_strings; i = 0; while (*opt_name) { uint8_t code = dhcp_optflags[i].code; BITMAP *found_ptr = &FOUND_OPTS(code); BITMAP found_mask = BMASK(code); if (!(*found_ptr & found_mask)) goto next; *found_ptr &= ~found_mask; /* leave only unknown options */ temp = udhcp_get_option(packet, code); *curr = xmalloc_optname_optval(temp, &dhcp_optflags[i], opt_name); putenv(*curr++); if (code == DHCP_SUBNET) { /* Subnet option: make things like "$ip/$mask" possible */ uint32_t subnet; move_from_unaligned32(subnet, temp); *curr = xasprintf("mask=%d", mton(subnet)); putenv(*curr++); } next: opt_name += strlen(opt_name) + 1; i++; } if (packet->siaddr_nip) { *curr = xmalloc(sizeof("siaddr=255.255.255.255")); sprint_nip(*curr, "siaddr=", (uint8_t *) &packet->siaddr_nip); putenv(*curr++); } if (!(overload & FILE_FIELD) && packet->file[0]) { /* watch out for invalid packets */ *curr = xasprintf("boot_file=%."DHCP_PKT_FILE_LEN_STR"s", packet->file); putenv(*curr++); } if (!(overload & SNAME_FIELD) && packet->sname[0]) { /* watch out for invalid packets */ *curr = xasprintf("sname=%."DHCP_PKT_SNAME_LEN_STR"s", packet->sname); putenv(*curr++); } /* Handle unknown options */ for (i = 0; i < 256;) { BITMAP bitmap = FOUND_OPTS(i); if (!bitmap) { i += BBITS; continue; } if (bitmap & BMASK(i)) { unsigned len, ofs; temp = udhcp_get_option(packet, i); /* udhcp_get_option returns ptr to data portion, * need to go back to get len */ len = temp[-OPT_DATA + OPT_LEN]; *curr = xmalloc(sizeof("optNNN=") + 1 + len*2); ofs = sprintf(*curr, "opt%u=", i); bin2hex(*curr + ofs, (void*) temp, len)[0] = '\0'; putenv(*curr++); } i++; } return envp; } /* Call a script with a par file and env vars */ static void udhcp_run_script(struct dhcp_packet *packet, const char *name, const char *action) { char **envp, **curr; char *argv[4]; if (client_config.script == NULL) return; envp = fill_envp(packet); /* call script */ log1("Executing %s %s", client_config.script, name); argv[0] = (char*) client_config.script; argv[1] = (char*) name; argv[2] = action; argv[3] = NULL; spawn_and_wait(argv); for (curr = envp; *curr; curr++) { log2(" %s", *curr); bb_unsetenv_and_free(*curr); } free(envp); } /*** Sending/receiving packets ***/ static ALWAYS_INLINE uint32_t random_xid(void) { return rand(); } /* Initialize the packet with the proper defaults */ static void init_packet(struct dhcp_packet *packet, char type) { uint16_t secs; /* Fill in: op, htype, hlen, cookie fields; message type option: */ udhcp_init_header(packet, type); packet->xid = random_xid(); client_config.last_secs = monotonic_sec(); if (client_config.first_secs == 0) client_config.first_secs = client_config.last_secs; secs = client_config.last_secs - client_config.first_secs; packet->secs = htons(secs); memcpy(packet->chaddr, client_config.client_mac, 6); if (client_config.clientid) udhcp_add_binary_option(packet, client_config.clientid); } static void add_client_options(struct dhcp_packet *packet, int flag) { uint8_t c; int i, end, len; udhcp_add_simple_option(packet, DHCP_MAX_SIZE, htons(IP_UDP_DHCP_SIZE)); /* Add a "param req" option with the list of options we'd like to have * from stubborn DHCP servers. Pull the data from the struct in common.c. * No bounds checking because it goes towards the head of the packet. */ end = udhcp_end_option(packet->options); len = 0; for (i = 0; (c = dhcp_optflags[i].code) != 0; i++) { if (( (dhcp_optflags[i].flags & OPTION_REQ) && !client_config.no_default_options ) || (client_config.opt_mask[c >> 3] & (1 << (c & 7))) ) { packet->options[end + OPT_DATA + len] = c; len++; } } if (len) { packet->options[end + OPT_CODE] = DHCP_PARAM_REQ; packet->options[end + OPT_LEN] = len; packet->options[end + OPT_DATA + len] = DHCP_END; } if (client_config.vendorclass) udhcp_add_binary_option(packet, client_config.vendorclass); if (client_config.hostname) udhcp_add_binary_option(packet, client_config.hostname); if (client_config.fqdn) udhcp_add_binary_option(packet, client_config.fqdn); /* Request broadcast replies, even though we have no IP addr, modified by wangfuyu, 20140910 */ if (option_mask32 & OPT_B) packet->flags |= htons(flag); /* Request broadcast replies if we have no IP addr */ /* if ((option_mask32 & OPT_B) && packet->ciaddr == 0) packet->flags |= htons(BROADCAST_FLAG); */ /* Add -x options if any */ { struct option_set *curr = client_config.options; while (curr) { udhcp_add_binary_option(packet, curr->data); curr = curr->next; } // if (client_config.sname) // strncpy((char*)packet->sname, client_config.sname, sizeof(packet->sname) - 1); // if (client_config.boot_file) // strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1); } } /* RFC 2131 * 4.4.4 Use of broadcast and unicast * * The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM * messages, unless the client knows the address of a DHCP server. * The client unicasts DHCPRELEASE messages to the server. Because * the client is declining the use of the IP address supplied by the server, * the client broadcasts DHCPDECLINE messages. * * When the DHCP client knows the address of a DHCP server, in either * INIT or REBOOTING state, the client may use that address * in the DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address. * The client may also use unicast to send DHCPINFORM messages * to a known DHCP server. If the client receives no response to DHCP * messages sent to the IP address of a known DHCP server, the DHCP * client reverts to using the IP broadcast address. */ static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet) { return udhcp_send_raw_packet(packet, /*src*/ INADDR_ANY, CLIENT_PORT, /*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); } /* Broadcast a DHCP discover packet to the network, with an optionally requested IP */ /* NOINLINE: limit stack usage in caller */ static NOINLINE int send_discover(uint32_t xid, uint32_t requested, int flag) { struct dhcp_packet packet; static int msgs = 0; /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), * client-id option (unless -C), message type option: */ init_packet(&packet, DHCPDISCOVER); packet.xid = xid; if (requested) { udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); } /* Add options: maxsize, * optionally: hostname, fqdn, vendorclass, * "param req" option according to -O, options specified with -x */ add_client_options(&packet, flag); if (msgs++ < 3) msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Sending discover..."); return raw_bcast_from_client_config_ifindex(&packet); } /* Broadcast a DHCP request message */ /* RFC 2131 3.1 paragraph 3: * "The client _broadcasts_ a DHCPREQUEST message..." */ /* NOINLINE: limit stack usage in caller */ static NOINLINE int send_select(uint32_t xid, uint32_t server, uint32_t requested) { struct dhcp_packet packet; struct in_addr addr; /* * RFC 2131 4.3.2 DHCPREQUEST message * ... * If the DHCPREQUEST message contains a 'server identifier' * option, the message is in response to a DHCPOFFER message. * Otherwise, the message is a request to verify or extend an * existing lease. If the client uses a 'client identifier' * in a DHCPREQUEST message, it MUST use that same 'client identifier' * in all subsequent messages. If the client included a list * of requested parameters in a DHCPDISCOVER message, it MUST * include that list in all subsequent messages. */ /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), * client-id option (unless -C), message type option: */ init_packet(&packet, DHCPREQUEST); packet.xid = xid; udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); /* Add options: maxsize, * optionally: hostname, fqdn, vendorclass, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet, BROADCAST_FLAG); addr.s_addr = requested; msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Sending select for %s...", inet_ntoa(addr)); return raw_bcast_from_client_config_ifindex(&packet); } /* Unicast or broadcast a DHCP renew message */ /* NOINLINE: limit stack usage in caller */ static NOINLINE int send_renew(uint32_t xid, uint32_t server, uint32_t ciaddr) { struct dhcp_packet packet; /* * RFC 2131 4.3.2 DHCPREQUEST message * ... * DHCPREQUEST generated during RENEWING state: * * 'server identifier' MUST NOT be filled in, 'requested IP address' * option MUST NOT be filled in, 'ciaddr' MUST be filled in with * client's IP address. In this situation, the client is completely * configured, and is trying to extend its lease. This message will * be unicast, so no relay agents will be involved in its * transmission. Because 'giaddr' is therefore not filled in, the * DHCP server will trust the value in 'ciaddr', and use it when * replying to the client. */ /* Fill in: op, htype, hlen, cookie, chaddr fields, * random xid field (we override it below), * client-id option (unless -C), message type option: */ init_packet(&packet, DHCPREQUEST); packet.xid = xid; packet.ciaddr = ciaddr; /* Add options: maxsize, * optionally: hostname, fqdn, vendorclass, * "param req" option according to -O, and options specified with -x */ add_client_options(&packet, BROADCAST_FLAG); msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Sending renew..."); if (server) return udhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); return raw_bcast_from_client_config_ifindex(&packet); } #if 1 //#if ENABLE_FEATURE_UDHCPC_ARPING /* Broadcast a DHCP decline message */ /* NOINLINE: limit stack usage in caller */ static NOINLINE int send_decline(uint32_t xid, uint32_t server, uint32_t requested) { struct dhcp_packet packet; /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, * client-id option (unless -C), message type option: */ init_packet(&packet, DHCPDECLINE); /* RFC 2131 says DHCPDECLINE's xid is randomly selected by client, * but in case the server is buggy and wants DHCPDECLINE's xid * to match the xid which started entire handshake, * we use the same xid we used in initial DHCPDISCOVER: */ packet.xid = xid; /* DHCPDECLINE uses "requested ip", not ciaddr, to store offered IP */ udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Sending decline..."); return raw_bcast_from_client_config_ifindex(&packet); } #endif /* Unicast a DHCP release message */ static int send_release(uint32_t server, uint32_t ciaddr) { struct dhcp_packet packet; /* Fill in: op, htype, hlen, cookie, chaddr, random xid fields, * client-id option (unless -C), message type option: */ init_packet(&packet, DHCPRELEASE); /* DHCPRELEASE uses ciaddr, not "requested ip", to store IP being released */ packet.ciaddr = ciaddr; udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Sending release..."); return udhcp_send_kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT); } /* Returns -1 on errors that are fatal for the socket, -2 for those that aren't */ /* NOINLINE: limit stack usage in caller */ static NOINLINE int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd) { int bytes; int nocsum = 0; struct ip_udp_dhcp_packet packet; uint16_t check; unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; struct iovec iov = { .iov_base = &packet, .iov_len = sizeof(packet), }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsgbuf, .msg_controllen = sizeof(cmsgbuf), }; struct cmsghdr *cmsg; memset(&packet, 0, sizeof(packet)); do { bytes = recvmsg(fd, &msg, 0); } while (bytes < 0 && errno == EINTR); if (bytes < 0) { log1("Packet read error, ignoring"); /* NB: possible down interface, etc. Caller should pause. */ return bytes; /* returns -1 */ } for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_PACKET && cmsg->cmsg_type == PACKET_AUXDATA) { struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); nocsum = aux->tp_status & TP_STATUS_CSUMNOTREADY; } } if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) { log1("Packet is too short, ignoring"); return -2; } if (bytes < ntohs(packet.ip.tot_len)) { /* packet is bigger than sizeof(packet), we did partial read */ log1("Oversized packet, ignoring"); return -2; } /* ignore any extra garbage bytes */ bytes = ntohs(packet.ip.tot_len); /* make sure its the right packet for us, and that it passes sanity checks */ if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2) || packet.udp.dest != htons(CLIENT_PORT) /* || bytes > (int) sizeof(packet) - can't happen */ || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip)) ) { log1("Unrelated/bogus packet, ignoring"); return -2; } /* verify IP checksum */ check = packet.ip.check; packet.ip.check = 0; if (check != udhcp_checksum(&packet.ip, sizeof(packet.ip))) { log1("Bad IP header checksum, ignoring"); return -2; } /* verify UDP checksum. IP header has to be modified for this */ memset(&packet.ip, 0, offsetof(struct iphdr, protocol)); /* ip.xx fields which are not memset: protocol, check, saddr, daddr */ packet.ip.tot_len = packet.udp.len; /* yes, this is needed */ check = packet.udp.check; packet.udp.check = 0; if (!nocsum && check && check != udhcp_checksum(&packet, bytes)) { log1("Packet with bad UDP checksum received, ignoring"); return -2; } memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp))); if (dhcp_pkt->cookie != htonl(DHCP_MAGIC)) { msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Packet with bad magic, ignoring"); return -2; } log1("Got valid DHCP packet"); udhcp_dump_packet(dhcp_pkt); return bytes - (sizeof(packet.ip) + sizeof(packet.udp)); } /*** Main ***/ static int sockfd = -1; #define LISTEN_NONE 0 #define LISTEN_KERNEL 1 #define LISTEN_RAW 2 static smallint listen_mode; /* initial state: (re)start DHCP negotiation */ #define INIT_SELECTING 0 /* discover was sent, DHCPOFFER reply received */ #define REQUESTING 1 /* select/renew was sent, DHCPACK reply received */ #define BOUND 2 /* half of lease passed, want to renew it by sending unicast renew requests */ #define RENEWING 3 /* renew requests were not answered, lease is almost over, send broadcast renew */ #define REBINDING 4 /* manually requested renew (SIGUSR1) */ #define RENEW_REQUESTED 5 /* release, possibly manually requested (SIGUSR2) */ #define RELEASED 6 static smallint state; static int udhcp_raw_socket(int ifindex) { int fd; struct sockaddr_ll sock; int val; /* * Comment: * * I've selected not to see LL header, so BPF doesn't see it, too. * The filter may also pass non-IP and non-ARP packets, but we do * a more complete check when receiving the message in userspace. * * and filter shamelessly stolen from: * * http://www.flamewarmaster.de/software/dhcpclient/ * * There are a few other interesting ideas on that page (look under * "Motivation"). Use of netlink events is most interesting. Think * of various network servers listening for events and reconfiguring. * That would obsolete sending HUP signals and/or make use of restarts. * * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>. * License: GPL v2. * * TODO: make conditional? */ static const struct sock_filter filter_instr[] = { /* load 9th byte (protocol) */ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), /* jump to L1 if it is IPPROTO_UDP, else to L4 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6), /* L1: load halfword from offset 6 (flags and frag offset) */ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), /* jump to L4 if any bits in frag offset field are set, else to L2 */ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0), /* L2: skip IP header (load index reg with header len) */ BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* load udp destination port from halfword[header_len + 2] */ BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2), /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1), /* L3: accept packet */ BPF_STMT(BPF_RET|BPF_K, 0xffffffff), /* L4: discard packet */ BPF_STMT(BPF_RET|BPF_K, 0), }; static const struct sock_fprog filter_prog = { .len = sizeof(filter_instr) / sizeof(filter_instr[0]), /* casting const away: */ .filter = (struct sock_filter *) filter_instr, }; log1("Opening raw socket on ifindex %d", ifindex); //log2? fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); log1("Got raw socket fd %d", fd); //log2? sock.sll_family = AF_PACKET; sock.sll_protocol = htons(ETH_P_IP); sock.sll_ifindex = ifindex; xbind(fd, (struct sockaddr *) &sock, sizeof(sock)); if (CLIENT_PORT == 68) { /* Use only if standard port is in use */ /* Ignoring error (kernel may lack support for this) */ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) >= 0) log1("Attached filter to raw socket fd %d", fd); // log? } val = 1; if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, &val, sizeof(val)) < 0) { if (errno != ENOPROTOOPT) log1("Failed to set auxiliary packet data for socket fd %d", fd); } log1("Created raw socket"); return fd; } static void change_listen_mode(int new_mode) { log1("Entering listen mode: %s", new_mode != LISTEN_NONE ? (new_mode == LISTEN_KERNEL ? "kernel" : "raw") : "none" ); listen_mode = new_mode; if (sockfd >= 0) { close(sockfd); sockfd = -1; } if (new_mode == LISTEN_KERNEL) sockfd = udhcp_listen_socket(/*INADDR_ANY,*/ CLIENT_PORT, client_config.interface); else if (new_mode != LISTEN_NONE) sockfd = udhcp_raw_socket(client_config.ifindex); /* else LISTEN_NONE: sockfd stays closed */ } /* Called only on SIGUSR1 */ static void perform_renew(void) { msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Performing a DHCP renew"); switch (state) { case BOUND: change_listen_mode(LISTEN_KERNEL); case RENEWING: case REBINDING: state = RENEW_REQUESTED; break; case RENEW_REQUESTED: /* impatient are we? fine, square 1 */ case REQUESTING: case RELEASED: change_listen_mode(LISTEN_RAW); state = INIT_SELECTING; break; case INIT_SELECTING: break; } } static void perform_release(uint32_t requested_ip, uint32_t server_addr) { char buffer[sizeof("255.255.255.255")]; struct in_addr temp_addr; /* send release packet */ if (state == BOUND || state == RENEWING || state == REBINDING) { temp_addr.s_addr = server_addr; strcpy(buffer, inet_ntoa(temp_addr)); temp_addr.s_addr = requested_ip; msglog(LOG_PRIO_NOTICE, DHCPC_MODU_NAME, "Unicasting a release of %s to %s", inet_ntoa(temp_addr), buffer); send_release(server_addr, requested_ip); /* unicast */ udhcp_run_script(NULL, "deconfig", "release"); } msglog(LOG_PRIO_NOTICE, DHCPC_MODU_NAME, "Entering released state"); change_listen_mode(LISTEN_NONE); state = RELEASED; } static uint8_t* alloc_dhcp_option(int code, const char *str, int extra) { uint8_t *storage; int len = strnlen(str, 255); storage = xzalloc(len + extra + OPT_DATA); storage[OPT_CODE] = code; storage[OPT_LEN] = len + extra; memcpy(storage + extra + OPT_DATA, str, len); return storage; } #if BB_MMU static void client_background(void) { bb_daemonize(0); logmode &= ~LOGMODE_STDIO; /* rewrite pidfile, as our pid is different now */ write_pidfile(client_config.pidfile); } #endif //usage:#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 //usage:# define IF_UDHCP_VERBOSE(...) __VA_ARGS__ //usage:#else //usage:# define IF_UDHCP_VERBOSE(...) //usage:#endif //usage:#define udhcpc_trivial_usage //usage: "[-fbnq"IF_UDHCP_VERBOSE("v")"oCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n" //usage: " [-H HOSTNAME] [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]") "[-k]" //usage:#define udhcpc_full_usage "\n" //usage: IF_LONG_OPTS( //usage: "\n -i,--interface IFACE Interface to use (default eth0)" //usage: "\n -p,--pidfile FILE Create pidfile" //usage: "\n -s,--script PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" //usage: "\n -B,--broadcast Request broadcast replies" //usage: "\n -t,--retries N Send up to N discover packets" //usage: "\n -T,--timeout N Pause between packets (default 3 seconds)" //usage: "\n -A,--tryagain N Wait N seconds after failure (default 20)" //usage: "\n -f,--foreground Run in foreground" //usage: USE_FOR_MMU( //usage: "\n -b,--background Background if lease is not obtained" //usage: ) //usage: "\n -n,--now Exit if lease is not obtained" //usage: "\n -q,--quit Exit after obtaining lease" //usage: "\n -R,--release Release IP on exit" //usage: "\n -S,--syslog Log to syslog too" //usage: IF_FEATURE_UDHCP_PORT( //usage: "\n -P,--client-port N Use port N (default 68)" //usage: ) //usage: IF_FEATURE_UDHCPC_ARPING( //usage: "\n -a,--arping Use arping to validate offered address" //usage: ) //usage: "\n -O,--request-option OPT Request option OPT from server (cumulative)" //usage: "\n -o,--no-default-options Don't request any options (unless -O is given)" //usage: "\n -r,--request IP Request this IP address" //usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" //usage: "\n Examples of string, numeric, and hex byte opts:" //usage: "\n -x hostname:bbox - option 12" //usage: "\n -x lease:3600 - option 51 (lease time)" //usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" //usage: "\n -F,--fqdn NAME Ask server to update DNS mapping for NAME" //usage: "\n -H,-h,--hostname NAME Send NAME as client hostname (default none)" //usage: "\n -V,--vendorclass VENDOR Vendor identifier (default 'udhcp VERSION')" //usage: "\n -C,--clientid-none Don't send MAC as client identifier" //usage: IF_UDHCP_VERBOSE( //usage: "\n -v Verbose" //usage: ) //usage: "\n -k,--keep-request Keep request same ip even received NAK" //usage: ) //usage: IF_NOT_LONG_OPTS( //usage: "\n -i IFACE Interface to use (default eth0)" //usage: "\n -p FILE Create pidfile" //usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" //usage: "\n -B Request broadcast replies" //usage: "\n -t N Send up to N discover packets" //usage: "\n -T N Pause between packets (default 3 seconds)" //usage: "\n -A N Wait N seconds (default 20) after failure" //usage: "\n -f Run in foreground" //usage: USE_FOR_MMU( //usage: "\n -b Background if lease is not obtained" //usage: ) //usage: "\n -n Exit if lease is not obtained" //usage: "\n -q Exit after obtaining lease" //usage: "\n -R Release IP on exit" //usage: "\n -S Log to syslog too" //usage: IF_FEATURE_UDHCP_PORT( //usage: "\n -P N Use port N (default 68)" //usage: ) //usage: IF_FEATURE_UDHCPC_ARPING( //usage: "\n -a Use arping to validate offered address" //usage: ) //usage: "\n -O OPT Request option OPT from server (cumulative)" //usage: "\n -o Don't request any options (unless -O is given)" //usage: "\n -r IP Request this IP address" //usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" //usage: "\n Examples of string, numeric, and hex byte opts:" //usage: "\n -x hostname:bbox - option 12" //usage: "\n -x lease:3600 - option 51 (lease time)" //usage: "\n -x 0x3d:0100BEEFC0FFEE - option 61 (client id)" //usage: "\n -F NAME Ask server to update DNS mapping for NAME" //usage: "\n -H,-h NAME Send NAME as client hostname (default none)" //usage: "\n -V VENDOR Vendor identifier (default 'udhcp VERSION')" //usage: "\n -C Don't send MAC as client identifier" //usage: IF_UDHCP_VERBOSE( //usage: "\n -v Verbose" //usage: ) //usage: "\n -k,--keep-request Keep request same ip even received NAK" //usage: ) //usage: "\nSignals:" //usage: "\n USR1 Renew current lease" //usage: "\n USR2 Release current lease" int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpc_main(int argc UNUSED_PARAM, char **argv) { uint8_t *temp, *message; const char *str_V, *str_h, *str_F, *str_r; IF_FEATURE_UDHCP_PORT(char *str_P;) void *clientid_mac_ptr; llist_t *list_O = NULL; llist_t *list_x = NULL; int tryagain_timeout = 20; int discover_retries = 5; int timeout_circle[5] = {2, 2, 4, 2, 2}; int discover_timeout = 3; int invert_bit = 3; int timeout_increment = 30; int retrans_renew = 0; int retrans_rebind = 0; uint32_t server_addr = server_addr; /* for compiler */ uint32_t requested_ip = 0; uint32_t xid = 0; uint32_t lease_seconds = 0; /* can be given as 32-bit quantity */ int packet_num; int timeout; /* must be signed */ unsigned already_waited_sec; unsigned opt; int max_fd; int retval; struct timeval tv; struct dhcp_packet packet; fd_set rfds; int flag = 0; int lease_notify = 0; char buf[512] = {0}; int keep_request = 0; int decline_times = 0; /* Default options */ IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;) IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;) client_config.interface = "eth0"; client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT; str_V = "udhcp "BB_VER; /* Parse command line */ /* O,x: list; -T,-t,-A take numeric param */ opt_complementary = "O::x::T+:t+:A+" #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 ":vv" #endif ; IF_LONG_OPTS(applet_long_options = udhcpc_longopts;) opt = getopt32(argv, "CV:H:h:F:i:np:qRr:s:T:t:SA:O:ox:fB" USE_FOR_MMU("b") IF_FEATURE_UDHCPC_ARPING("a") IF_FEATURE_UDHCP_PORT("P:") #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 "v" #endif "k" , &str_V, &str_h, &str_h, &str_F , &client_config.interface, &client_config.pidfile, &str_r /* i,p */ , &client_config.script /* s */ , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ , &list_O , &list_x IF_FEATURE_UDHCP_PORT(, &str_P) #if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1 , &dhcp_verbose #endif ); if (opt & (OPT_h|OPT_H)) client_config.hostname = alloc_dhcp_option(DHCP_HOST_NAME, str_h, 0); if (opt & OPT_F) { /* FQDN option format: [0x51][len][flags][0][0]<fqdn> */ client_config.fqdn = alloc_dhcp_option(DHCP_FQDN, str_F, 3); /* Flag bits: 0000NEOS * S: 1 = Client requests server to update A RR in DNS as well as PTR * O: 1 = Server indicates to client that DNS has been updated regardless * E: 1 = Name is in DNS format, i.e. <4>host<6>domain<3>com<0>, * not "host.domain.com". Format 0 is obsolete. * N: 1 = Client requests server to not update DNS (S must be 0 then) * Two [0] bytes which follow are deprecated and must be 0. */ client_config.fqdn[OPT_DATA + 0] = 0x1; /*client_config.fqdn[OPT_DATA + 1] = 0; - xzalloc did it */ /*client_config.fqdn[OPT_DATA + 2] = 0; */ } if (opt & OPT_r) requested_ip = inet_addr(str_r); #if ENABLE_FEATURE_UDHCP_PORT if (opt & OPT_P) { CLIENT_PORT = xatou16(str_P); SERVER_PORT = CLIENT_PORT - 1; } #endif if (opt & OPT_o) client_config.no_default_options = 1; while (list_O) { char *optstr = llist_pop(&list_O); unsigned n = bb_strtou(optstr, NULL, 0); if (errno || n > 254) { n = udhcp_option_idx(optstr); n = dhcp_optflags[n].code; } client_config.opt_mask[n >> 3] |= 1 << (n & 7); } while (list_x) { char *optstr = llist_pop(&list_x); char *colon = strchr(optstr, ':'); if (colon) *colon = ' '; /* now it looks similar to udhcpd's config file line: * "optname optval", using the common routine: */ udhcp_str2optset(optstr, &client_config.options); } if (udhcp_read_interface(client_config.interface, &client_config.ifindex, NULL, client_config.client_mac) ) { return 1; } clientid_mac_ptr = NULL; if (!(opt & OPT_C) && !udhcp_find_option(client_config.options, DHCP_CLIENT_ID)) { /* not suppressed and not set, set the default client ID */ client_config.clientid = alloc_dhcp_option(DHCP_CLIENT_ID, "", 7); client_config.clientid[OPT_DATA] = 1; /* type: ethernet */ clientid_mac_ptr = client_config.clientid + OPT_DATA+1; memcpy(clientid_mac_ptr, client_config.client_mac, 6); } if (str_V[0] != '\0') client_config.vendorclass = alloc_dhcp_option(DHCP_VENDOR, str_V, 0); #if !BB_MMU /* on NOMMU reexec (i.e., background) early */ if (!(opt & OPT_f)) { bb_daemonize_or_rexec(0 /* flags */, argv); logmode = LOGMODE_NONE; } #endif if (opt & OPT_S) { openlog(applet_name, LOG_PID, LOG_DAEMON); logmode |= LOGMODE_SYSLOG; } if (opt & OPT_k) { keep_request = 1; } /* Make sure fd 0,1,2 are open */ bb_sanitize_stdio(); /* Equivalent of doing a fflush after every \n */ setlinebuf(stdout); /* Create pidfile */ write_pidfile(client_config.pidfile); /* Goes to stdout (unless NOMMU) and possibly syslog */ msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "%s (v"BB_VER") started", applet_name); /* Set up the signal pipe */ udhcp_sp_setup(); /* We want random_xid to be random... */ srand(monotonic_us()); state = INIT_SELECTING; udhcp_run_script(NULL, "deconfig", "init"); change_listen_mode(LISTEN_RAW); packet_num = 0; timeout = 0; already_waited_sec = 0; discover_retries = 5; /* Main event loop. select() waits on signal pipe and possibly * on sockfd. * "continue" statements in code below jump to the top of the loop. */ for (;;) { /* silence "uninitialized!" warning */ unsigned timestamp_before_wait = timestamp_before_wait; /* When running on a bridge, the ifindex may have changed (e.g. if * member interfaces were added/removed or if the status of the * bridge changed). * Workaround: refresh it here before processing the next packet */ udhcp_read_interface(client_config.interface, &client_config.ifindex, NULL, client_config.client_mac); //bb_error_msg("sockfd:%d, listen_mode:%d", sockfd, listen_mode); /* Was opening raw or udp socket here * if (listen_mode != LISTEN_NONE && sockfd < 0), * but on fast network renew responses return faster * than we open sockets. Thus this code is moved * to change_listen_mode(). Thus we open listen socket * BEFORE we send renew request (see "case BOUND:"). */ max_fd = udhcp_sp_fd_set(&rfds, sockfd); tv.tv_sec = timeout - already_waited_sec; tv.tv_usec = 0; retval = 0; /* If we already timed out, fall through with retval = 0, else... */ if ((int)tv.tv_sec > 0) { timestamp_before_wait = (unsigned)monotonic_sec(); log1("Waiting on select..."); retval = select(max_fd + 1, &rfds, NULL, NULL, &tv); if (retval < 0) { /* EINTR? A signal was caught, don't panic */ if (errno == EINTR) { already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; continue; } /* Else: an error occured, panic! */ bb_perror_msg_and_die("select"); } } /* If timeout dropped to zero, time to become active: * resend discover/renew/whatever */ if (retval == 0) { /* When running on a bridge, the ifindex may have changed * (e.g. if member interfaces were added/removed * or if the status of the bridge changed). * Refresh ifindex and client_mac: */ if (udhcp_read_interface(client_config.interface, &client_config.ifindex, NULL, client_config.client_mac) ) { return 1; /* iface is gone? */ } if (clientid_mac_ptr) memcpy(clientid_mac_ptr, client_config.client_mac, 6); /* We will restart the wait in any case */ already_waited_sec = 0; switch (state) { case INIT_SELECTING: if (packet_num == 0) xid = random_xid(); if (packet_num < invert_bit) { /* need broadcast response*/ send_discover(xid, requested_ip, BROADCAST_FLAG); } else { /* need unicast response*/ send_discover(xid, requested_ip, UNICAST_FLAG); } timeout = timeout_circle[packet_num]; packet_num++; if (packet_num != discover_retries) { continue; } leasefail: msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "zb debug lease_notify:%d\n", lease_notify); if (!lease_notify) { lease_notify = 1; udhcp_run_script(NULL, "leasefail", NULL); } #if BB_MMU /* -b is not supported on NOMMU */ if (opt & OPT_b) { /* background if no lease */ msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "No lease, forking to background"); client_background(); /* do not background again! */ opt = ((opt & ~OPT_b) | OPT_f); } else #endif if (opt & OPT_n) { /* abort if no lease */ msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "No lease, failing"); retval = 1; goto ret; } /* wait before trying again */ packet_num = 0; timeout = timeout_circle[packet_num]; continue; case REQUESTING: if (!discover_retries || packet_num < discover_retries) { /* send broadcast select packet */ send_select(xid, server_addr, requested_ip); timeout = discover_timeout; packet_num++; continue; } /* Timed out, go back to init state. * "discover...select...discover..." loops * were seen in the wild. Treat them similarly * to "no response to discover" case */ change_listen_mode(LISTEN_RAW); state = INIT_SELECTING; goto leasefail; case BOUND: /* 1/2 lease passed, enter renewing state */ state = RENEWING; client_config.first_secs = 0; /* make secs field count from 0 */ change_listen_mode(LISTEN_KERNEL); log1("Entering renew state"); /* fall right through */ case RENEW_REQUESTED: /* manual (SIGUSR1) renew */ case_RENEW_REQUESTED: case RENEWING: if (timeout > retrans_renew) { /* send an unicast renew request */ /* Sometimes observed to fail (EADDRNOTAVAIL) to bind * a new UDP socket for sending inside send_renew. * I hazard to guess existing listening socket * is somehow conflicting with it, but why is it * not deterministic then?! Strange. * Anyway, it does recover by eventually failing through * into INIT_SELECTING state. */ send_renew(xid, server_addr, requested_ip); timeout = retrans_renew; timeout >>= 1; continue; } /* retransfer request timeout */ else if (timeout >= 60) { send_renew(xid, server_addr, requested_ip); timeout >>= 1; continue; } /* Timed out, enter rebinding state */ log1("Entering rebinding state"); state = REBINDING; timeout += retrans_rebind; /* fall right through */ case REBINDING: /* Switch to bcast receive */ change_listen_mode(LISTEN_RAW); /* Lease is *really* about to run out, * try to find DHCP server using broadcast */ if (flag == 0 || timeout >= 60) { /* send a broadcast renew request */ send_renew(xid, 0 /*INADDR_ANY*/, requested_ip); timeout >>= 1; flag = 1; continue; } /* Timed out, enter init state */ msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Lease lost, entering init state"); udhcp_run_script(NULL, "deconfig", "rebind"); state = INIT_SELECTING; client_config.first_secs = 0; /* make secs field count from 0 */ /*timeout = 0; - already is */ packet_num = 0; continue; /* case RELEASED: */ } /* yah, I know, *you* say it would never happen */ timeout = INT_MAX; continue; /* back to main loop */ } /* if select timed out */ /* select() didn't timeout, something happened */ /* Is it a signal? */ /* note: udhcp_sp_read checks FD_ISSET before reading */ switch (udhcp_sp_read(&rfds)) { case SIGUSR1: client_config.first_secs = 0; /* make secs field count from 0 */ perform_renew(); if (state == RENEW_REQUESTED) goto case_RENEW_REQUESTED; /* Start things over */ packet_num = 0; /* Kill any timeouts, user wants this to hurry along */ timeout = 0; continue; case SIGUSR2: perform_release(requested_ip, server_addr); timeout = INT_MAX; continue; case SIGTERM: msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Received SIGTERM"); if (opt & OPT_R) /* release on quit */ perform_release(requested_ip, server_addr); goto ret0; } /* Is it a packet? */ if (listen_mode == LISTEN_NONE || !FD_ISSET(sockfd, &rfds)) continue; /* no */ { int len; /* A packet is ready, read it */ if (listen_mode == LISTEN_KERNEL) len = udhcp_recv_kernel_packet(&packet, sockfd); else len = udhcp_recv_raw_packet(&packet, sockfd); if (len == -1) { /* Error is severe, reopen socket */ msglog(LOG_PRIO_ERROR, DHCPC_MODU_NAME, "Read error: %s, reopening socket", strerror(errno)); sleep(discover_timeout); /* 3 seconds by default */ change_listen_mode(listen_mode); /* just close and reopen */ } /* If this packet will turn out to be unrelated/bogus, * we will go back and wait for next one. * Be sure timeout is properly decreased. */ already_waited_sec += (unsigned)monotonic_sec() - timestamp_before_wait; if (len < 0) continue; } if (packet.xid != xid) { log1("xid %x (our is %x), ignoring packet", (unsigned)packet.xid, (unsigned)xid); continue; } /* Ignore packets that aren't for us */ if (packet.hlen != 6 || memcmp(packet.chaddr, client_config.client_mac, 6) != 0 ) { //FIXME: need to also check that last 10 bytes are zero log1("chaddr does not match, ignoring packet"); // log2? continue; } message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE); if (message == NULL) { bb_error_msg("no message type option, ignoring packet"); continue; } switch (state) { case INIT_SELECTING: /* Must be a DHCPOFFER to one of our xid's */ if (*message == DHCPOFFER) { msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Received DHCP-OFFER"); /* TODO: why we don't just fetch server's IP from IP header? */ temp = udhcp_get_option(&packet, DHCP_SERVER_ID); if (!temp) { bb_error_msg("no server ID, ignoring packet"); continue; /* still selecting - this server looks bad */ } /* it IS unaligned sometimes, don't "optimize" */ move_from_unaligned32(server_addr, temp); /*xid = packet.xid; - already is */ if ((1 == keep_request) && (0 != inet_addr(str_r))) { requested_ip = inet_addr(str_r); } else { requested_ip = packet.yiaddr; } /* enter requesting state */ state = REQUESTING; timeout = 0; packet_num = 0; already_waited_sec = 0; } continue; case REQUESTING: case RENEWING: case RENEW_REQUESTED: case REBINDING: if (*message == DHCPACK) { msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Received DHCP-ACK"); temp = udhcp_get_option(&packet, DHCP_LEASE_TIME); if (!temp) { bb_error_msg("no lease time with ACK, using 1 hour lease"); lease_seconds = 60 * 60; } else { /* it IS unaligned sometimes, don't "optimize" */ move_from_unaligned32(lease_seconds, temp); lease_seconds = ntohl(lease_seconds); lease_seconds &= 0x0fffffff; /* paranoia: must not be prone to overflows */ if (lease_seconds < 10) /* and not too small */ lease_seconds = 10; } #if 1 //#if ENABLE_FEATURE_UDHCPC_ARPING /* RFC 2131 3.1 paragraph 5: * "The client receives the DHCPACK message with configuration * parameters. The client SHOULD perform a final check on the * parameters (e.g., ARP for allocated network address), and notes * the duration of the lease specified in the DHCPACK message. At this * point, the client is configured. If the client detects that the * address is already in use (e.g., through the use of ARP), * the client MUST send a DHCPDECLINE message to the server and restarts * the configuration process..." */ if (!arpping(packet.yiaddr, NULL, (uint32_t) 0, client_config.client_mac, client_config.interface, 2000) ) { msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Offered address is in use " "(got ARP reply), declining"); send_decline(xid, server_addr, packet.yiaddr); if (state != REQUESTING) udhcp_run_script(NULL, "deconfig", "addr_in_use"); change_listen_mode(LISTEN_RAW); state = INIT_SELECTING; client_config.first_secs = 0; /* make secs field count from 0 */ if (1 == keep_request) { requested_ip = inet_addr(str_r); } else { requested_ip = 0; } timeout = tryagain_timeout; packet_num = 0; already_waited_sec = 0; continue; /* back to main loop */ } #endif /* server offer a IP that we don't wan't, send decline for another */ if ((1 == keep_request) && (0 != inet_addr(str_r)) && (packet.yiaddr != inet_addr(str_r))) { decline_times ++; if (decline_times > 3) { /* server refuse us too many times, give up, wait for some time and then request again */ change_listen_mode(LISTEN_RAW); sleep(DEFAULT_KEEP_WAIT_TIME); state = REQUESTING; requested_ip = inet_addr(str_r); /* keep request the same ipaddr */ client_config.first_secs = 0; timeout = 0; already_waited_sec = 0; continue; /* back to main loop */ } else { send_decline(xid, server_addr, packet.yiaddr); change_listen_mode(LISTEN_RAW); state = INIT_SELECTING; client_config.first_secs = 0; requested_ip = inet_addr(str_r); timeout = tryagain_timeout; packet_num = 0; already_waited_sec = 0; continue; /* back to main loop */ } } /* enter bound state */ timeout = lease_seconds / 2; { struct in_addr temp_addr; temp_addr.s_addr = packet.yiaddr; msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Lease of %s obtained, lease time %u", inet_ntoa(temp_addr), (unsigned)lease_seconds); } flag = 0; /* retrans_renew = 3/8 */ retrans_renew = timeout / 2 + timeout / 4; /* retrans_rebind = 1/8 */ retrans_rebind = timeout / 4; requested_ip = packet.yiaddr; udhcp_run_script(&packet, state == REQUESTING ? "bound" : "renew", NULL); lease_notify = 0; state = BOUND; change_listen_mode(LISTEN_NONE); if (opt & OPT_q) { /* quit after lease */ if (opt & OPT_R) /* release on quit */ perform_release(requested_ip, server_addr); goto ret0; } /* future renew failures should not exit (JM) */ opt &= ~OPT_n; #if BB_MMU /* NOMMU case backgrounded earlier */ if (!(opt & OPT_f)) { client_background(); /* do not background again! */ opt = ((opt & ~OPT_b) | OPT_f); } #endif already_waited_sec = 0; continue; /* back to main loop */ } if (*message == DHCPNAK) { if (1 == keep_request) { change_listen_mode(LISTEN_RAW); sleep(DEFAULT_KEEP_WAIT_TIME); state = REQUESTING; requested_ip = inet_addr(str_r); /* keep request the same ipaddr */ client_config.first_secs = 0; timeout = 0; already_waited_sec = 0; } else { /* return to init state */ msglog(LOG_PRIO_INFO, DHCPC_MODU_NAME, "Received DHCP-NAK"); udhcp_run_script(&packet, "nak", NULL); if (state != REQUESTING) udhcp_run_script(NULL, "deconfig", "nak"); change_listen_mode(LISTEN_RAW); sleep(3); /* avoid excessive network traffic */ state = INIT_SELECTING; client_config.first_secs = 0; /* make secs field count from 0 */ requested_ip = 0; timeout = 0; packet_num = 0; already_waited_sec = 0; } } continue; /* case BOUND: - ignore all packets */ /* case RELEASED: - ignore all packets */ } /* back to main loop */ } /* for (;;) - main loop ends */ ret0: retval = 0; ret: /*if (client_config.pidfile) - remove_pidfile has its own check */ remove_pidfile(client_config.pidfile); return retval; }
12-03
/****************************************************************************** \* Copyright (c) 2018-2020 TP-Link Systems Inc. * \* 文件名称: onvif_common.h \* 版 本: 1.0 \* 摘 要: ONVIF 通用函数及定义头文件 \* 作 者: liyijie<liyijie@tp-link.com.cn> \* 创建时间: 2018-10-30 ******************************************************************************/ \#ifndef __ONVIF_COMMON_H__ \#define __ONVIF_COMMON_H__ \#include <arpa/inet.h> \#include "nsd_common.h" \#include "../ipcd/api.h" \#define ONVIF_DEBUG(fmt, ...) NSD_DEBUG(NSD_ONVIF, "[ONVIF]" fmt, ##__VA_ARGS__) \#define ONVIF_WARN(fmt, ...) NSD_WARN(NSD_ONVIF, "[ONVIF]" fmt , ##__VA_ARGS__) \#define ONVIF_ERROR(fmt, ...) NSD_ERROR(NSD_ONVIF, "[ONVIF]" fmt , ##__VA_ARGS__) \#define ONVIF_INFO(fmt, ...) NSD_LOG(LOG_INFO, LOG_THIRDPARTY, "[ONVIF]" fmt, ##__VA_ARGS__) \#ifdef __ONVIF_TRACE__ \#define ONVIF_TRACE(fmt, ...) ONVIF_DEBUG(fmt, ##__VA_ARGS__) \#else \#define ONVIF_TRACE(fmt, ...) \#endif \#define ONVIF_FREE SAFE_FREE \#define ONVIF_MALLOC SAFE_MALLOC \#define ONVIF_STRDUP(str) strdup((str)) \#define TOLOWER(c) (char)((c >= 'A' && c <= 'Z') ? (c | 0x20) : c) \#define TOUPPER(c) (char)((c >= 'a' && c <= 'z') ? (c & ~0x20) : c) \#define ONVIF_TTL 4 \#define ONVIF_MCAST_IP "239.255.255.250" /* Params for Web Service */ \#define ONVIF_PORT_FOR_HTTP 80 \#define ONVIF_PORT_FOR_MAIN 2020 \#define ONVIF_PORT_FOR_DISCV 3702 \#define ONVIF_PORT_FOR_EVENT_BASE 1024 /* Prefix of UUID */ \#define UUID_PREFIX_FMAT "3fa1fe68-b915-4053-a3e1-" \#define UUID_PREFIX_UFMAT "3fa1fe68b9154053a3e1" /* msgq control message*/ \#define MSGQ_ON "on" \#define MSGQ_OFF "off" \#ifndef IFNAME_AP \#define IFNAME_AP "wlan1" \#endif /* Length */ \#define LEN_IFNAME 8 /* length of interface name */ \#define LEN_MTU 8 /* length of MTU str */ \#define LEN_IP 16 /* length of IP */ \#define LEN_MAC 18 /* length of mac */ \#define LEN_XADDR 128 /* length of XAddr */ \#define LEN_STREAM_URI 256 /* length of STREAM URI */ \#define LEN_SOME_INFO 256 /* length of SOME_INFO */ \#define LEN_ANY 4096 /* length of __any */ \#define LEN_INFO 128 /* length of infomation */ \#define LEN_TOKEN 128 /* length of TOKEN */ \#define LEN_NAME 128 /* length of user name */ \#define LEN_TIME 22 /* lendth of time string */ \#define LEN_RECOVERY_STR 9 \#define LEN_SHA_PWD 15 \#define LEN_RES_OPTION_NAME 8 \#define LEN_UUID 37 /* length of UUID */ \#define LEN_TAG 64 \#define LEN_VALUE 32 \#define LEN_INT 16 \#define LEN_BOOL 8 \#define STREAM_TYPE_LEN 8 \#define ONVIF_MAX_FREE_NUM 80 \#define ONVIF_DISCV_BUF_LEN (4 * 1024) /* receive buffer length */ /* 当前NVMP平台的事件通知机制可以取消订阅者数量的限制, \* 但为了防止恶意订阅消耗资源,按目前可支持接入的最大NVR数量限制订阅节点数量。*/ /* tev能力集中支持的最大节点数为10,创建节点是的节点数量限制应该同能力集一致, \* RTSP最大流数位8,节点限制位10足够 */ \#define EVENT_NODE_LIMIT 10 \#define ONVIF_SOCKET_RECV_BUF_SIZE (32 * 1024) \#define ONVIF_SOCKET_SEND_BUF_SIZE (32 * 1024) \#define MAX_TP_NAME_SPACE_COUNT 48 \#define MAX_NAMESPACE_ABBR_LEN 16 \#define MAX_NAMESPACE_LEN 128 struct onvif_namespace { char namespace_abbr[MAX_NAMESPACE_ABBR_LEN]; char namespace[MAX_NAMESPACE_LEN]; }; typedef struct _ONVIF_BUF { char *start; char *last; char *end; }ONVIF_BUF; /* 判断有nvr接入需要进行的处理 */ void onvif_nvr_access_event(); BOOL onvif_get_dhcp_enable(); S32 onvif_get_ip_str(char *ip_str, U32 out_len); S32 onvif_get_mac_str(char *mac_str, U32 out_len, BOOL need_separator); U32 onvif_get_mtu(); U32 onvif_get_netmask(); S32 onvif_create_buf(ONVIF_BUF *buf, S32 size); void onvif_free_buf(ONVIF_BUF *buf); S32 onvif_buf_append_string(ONVIF_BUF *buf, const char *str, S32 len); S32 onvif_str_append_chr(char *str, S32 buf_size, const char *fmt, ...); S32 onvif_send_udp_packet(S32 sock, U32 dst_ip, U16 port, char *send_buf, S32 send_len); S32 onvif_send_tcp_block(S32 sock, char *send_buf, S32 send_len); size_t onvif_base64_encode_alloc (const char *in, size_t inlen, char **out); BOOL onvif_base64_decode_alloc (const char *in, size_t inlen, char **out, size_t *outlen); S32 onvif_get_netdevice_name(S32 ifindex, char *ifname, S32 len); S32 onvif_get_discv_mode(); S32 onvif_set_discv_mode(S32 mode); S32 onvif_set_sock_rcvbuf(S32 sock, S32 buf_size); S32 onvif_set_sock_sndbuf(S32 sock, S32 buf_size); void onvif_init_supported_event_topic(); void free_ptrs(void *ptrs[], S32 num); S32 register_ptrs_to_free(void *ptr, void *ptrs[], S32 num); void *malloc_and_register(U32 size, void *ptrs[], S32 num); \#ifndef MALLOC_AND_REGISTER \#define MALLOC_AND_REGISTER(struct, cnt, ptrs, ptr_cnt_max) \ (struct *)malloc_and_register(sizeof(struct) * (cnt), ptrs, ptr_cnt_max) \#endif \#endif /* __ONVIF_COMMON_H__ */ /****************************************************************************** \* Copyright (c) 2018-2018 TP-Link Systems Inc. * \* Filename: onvif_discv.c \* Version: 1.0 \* Description: ONVIF 发现模块 \* Author: liyijie<liyijie@tp-link.com.cn> \* Date: 2018-10-30 ******************************************************************************/ \#include <stdio.h> \#include <stdlib.h> \#include <unistd.h> \#include <string.h> \#include <errno.h> \#include <sys/types.h> \#include <sys/socket.h> \#include <netinet/tcp.h> \#include <arpa/inet.h> \#include "slp_model.h" \#include "onvif_common.h" \#include "onvif_discv.h" \#include "soap_parse.h" \#include "soap_wsdd.h" LOCAL char g_onvif_discv_packet[ONVIF_DISCV_BUF_LEN + 1]; SOAP_CONTEXT *g_soap_for_discv = NULL; S32 g_index_for_discv = -1; LOCAL S32 onvif_recv_udp(SOAP_CONTEXT *soap, S32 sock, char *buf, ​ S32 buf_len, struct sockaddr_in *client) { S32 ret = 0; struct msghdr mh; struct iovec vector; char cmbuf[100] = {0}; struct cmsghdr *cmsg = NULL; struct in_pktinfo *pi = NULL; if (soap == NULL || sock < 0 || buf == NULL || client == NULL) { ​ return -1; } vector.iov_base = buf; vector.iov_len = buf_len; mh.msg_name = client; mh.msg_namelen = sizeof(struct sockaddr_in); mh.msg_iov = &vector; mh.msg_iovlen = 1; mh.msg_control = cmbuf; mh.msg_controllen = sizeof(cmbuf); mh.msg_flags = 0; ret = recvmsg(sock, &mh, 0); for (cmsg = CMSG_FIRSTHDR(&mh); cmsg != NULL; cmsg = CMSG_NXTHDR(&mh, cmsg)) { ​ if (cmsg->cmsg_level != IPPROTO_IP || cmsg->cmsg_type != IP_PKTINFO) ​ { ​ continue; ​ } ​ pi = (struct in_pktinfo *)CMSG_DATA(cmsg); ​ if (pi) ​ { ​ soap->ifindex = pi->ipi_ifindex; ​ } ​ else ​ { ​ soap->ifindex = -1; ​ } ​ break; } return ret; } /****************************************************************************** \* 函数名称: onvif_discover_handle() \* 函数描述: onvif discover报文处理函数 \* 输 入: sock -- socket \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 onvif_discover_handle(S32 sock) { S32 length = 0; struct sockaddr_in client; char *xml_buf = NULL; char **xml_str = NULL; char ip_str[LEN_IP] = {0}; if (sock < 0) { ​ ONVIF_ERROR("Invalid listen socket."); ​ return ERROR; } soap_init(g_soap_for_discv); g_soap_for_discv->use_udp = TRUE; memset((void *)&client, 0, sizeof(client)); length = onvif_recv_udp(g_soap_for_discv, sock, g_onvif_discv_packet, ​ ONVIF_DISCV_BUF_LEN, &client); if (length <= 0) { ​ ONVIF_ERROR("recv onvif discover msg failed."); ​ return ERROR; } ONVIF_TRACE("Recv onvif discover msg."); if (OK != onvif_get_ip_str(ip_str, LEN_IP)) { ​ ONVIF_WARN("onvif_get_ip_str failed."); ​ return ERROR; } /* 忽略本机发送的ONVIF包。 */ if (inet_addr(ip_str) == client.sin_addr.s_addr) { ​ ONVIF_TRACE("Recv onvif packet from self."); ​ return OK; } g_soap_for_discv->in_ip = client.sin_addr.s_addr; g_soap_for_discv->sin_port = client.sin_port; g_onvif_discv_packet[length] = '\0'; xml_buf = g_onvif_discv_packet; xml_str = (char **)&xml_buf; if (OK != soap_xml_parse(g_soap_for_discv, xml_str, length)) { ​ ONVIF_TRACE("Soap_xml_parse error."); ​ return ERROR; } if (g_soap_for_discv->tag[0] == '\0') { ​ ONVIF_TRACE("No request tag."); ​ return ERROR; } return soap_serve_request(g_soap_for_discv); } /****************************************************************************** \* 函数名称: onvif_discv_start() \* 函数描述: onvif discover处理模块启动函数 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 onvif_discv_start() { int on = 1; ONVIF_INFO("Onvif discovery start"); g_soap_for_discv = new_soap(); if (g_soap_for_discv == NULL) { ​ ONVIF_ERROR("Malloc g_soap_for_discv failed"); ​ return ERROR; } g_soap_for_discv->sock = sock_create_udp(INADDR_ANY, ONVIF_PORT_FOR_DISCV); if (g_soap_for_discv->sock < 0) { ​ ONVIF_ERROR("Sock_create_udp failed"); ​ free_soap(g_soap_for_discv); ​ g_soap_for_discv = NULL; ​ return ERROR; } if (sock_non_block(g_soap_for_discv->sock) < 0) { ​ ONVIF_WARN("Sock_non_block fail with errno:%d", errno); } g_index_for_discv = inet_add_socket(g_soap_for_discv->sock, ​ (void*)onvif_discover_handle, NULL, NULL); if (g_index_for_discv < 0) { ​ ONVIF_ERROR("Inet_socket_add failed"); ​ close(g_soap_for_discv->sock); ​ g_soap_for_discv->sock = -1; ​ free_soap(g_soap_for_discv); ​ g_soap_for_discv = NULL; ​ return ERROR; } if (0 > setsockopt(g_soap_for_discv->sock, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on))) { ​ ONVIF_WARN("setsockopt IP_PKTINFO failed."); } if (0 > onvif_set_sock_rcvbuf(g_soap_for_discv->sock, ONVIF_SOCKET_RECV_BUF_SIZE)) { ​ ONVIF_WARN("setsockopt SO_RCVBUF failed."); } if (0 > onvif_set_sock_sndbuf(g_soap_for_discv->sock, ONVIF_SOCKET_SEND_BUF_SIZE)) { ​ ONVIF_WARN("setsockopt SO_SNDBUF failed."); } if (sock_mcast_ttl(g_soap_for_discv->sock, ONVIF_TTL) < 0) { ​ ONVIF_WARN("setsockopt IP_MULTICAST_TTL fail with errno:%d", errno); } if (sock_mcast_group(g_soap_for_discv->sock, htonl(INADDR_ANY), inet_addr(ONVIF_MCAST_IP)) < 0) { ​ ONVIF_WARN("setsockopt IP_ADD_MEMBERSHIP fail with errno:%d", errno); } if (sock_mcast_if(g_soap_for_discv->sock, htonl(INADDR_ANY)) < 0) { ​ ONVIF_WARN("setsockopt IP_MULTICAST_IF fail with errno:%d", errno); } return OK; } /****************************************************************************** \* 函数名称: onvif_discv_stop() \* 函数描述: onvif discover处理模块停止函数,释放数据 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ void onvif_discv_stop() { if (g_index_for_discv > 0) { ​ inet_del_socket(g_index_for_discv); ​ g_index_for_discv = -1; } if (g_soap_for_discv->sock > 0) { ​ close(g_soap_for_discv->sock); } free_soap(g_soap_for_discv); g_soap_for_discv = NULL; return; } /****************************************************************************** \* 函数名称: onvif_discv_remove_socket() \* 函数描述: onvif discover处理模块固件升级时只移除socket,资源设备重启时回收 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ void onvif_discv_remove_socket() { if (g_index_for_discv > 0) { ​ inet_del_socket(g_index_for_discv); ​ g_index_for_discv = -1; } if (g_soap_for_discv->sock > 0) { ​ close(g_soap_for_discv->sock); ​ g_soap_for_discv->sock = -1; } return; } /****************************************************************************** \* 函数名称: onvif_hello() \* 函数描述: 发送onvif Hello报文 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 onvif_hello() { return soap_wsdd_hello(g_soap_for_discv); } /****************************************************************************** \* 函数名称: onvif_discv_handle_init() \* 函数描述: 注册onvif discover模块需要的soap处理函数 \* 输 入: N/A \* 输 出: N/A \* 返 回 值: N/A ******************************************************************************/ void onvif_discv_handle_init() { soap_wsdd_handle_init(); } /****************************************************************************** \* Copyright (c) 2018-2018 TP-Link Systems Inc. * \* Filename: soap_wsdd.c \* Version: 1.0 \* Description: soap 处理函数头文件 \* Author: liyijie<liyijie@tp-link.com.cn> \* Date: 2018-10-30 ******************************************************************************/ \#include <stdio.h> \#include <stdlib.h> \#include <unistd.h> \#include <string.h> \#include <arpa/inet.h> \#include "soap_global.h" \#include "soap_parse.h" \#include "soap_pack.h" \#include "soap_wsdd.h" /****************************************************************************** \* 函数名称: soap_check_scope() \* 函数描述: 检查输入的scope是否在设备保存的scope列表内 \* 输 入: scopes -- 输入的scope字串 \* 输 出: N/A \* 返 回 值: TRUE/FALSE ******************************************************************************/ LOCAL S32 soap_check_scope(char *scopes) { int i = 0; S32 flag = -1; S32 len = 0; char *s = NULL; char *e = NULL; char *t = NULL; SOAP_NODE *node = NULL; if (scopes == NULL) { ​ return 0; } if (strnlen(scopes, 1) == 0) { ​ return 0; } for (t = scopes; t && strnlen(t, 1); t++) { ​ e = (char *)strchr(t, ' '); ​ if (e != NULL) ​ { ​ len = e - t; ​ } ​ else ​ { ​ len = strlen(scopes) - (t - scopes); ​ } ​ if (len <= 0) ​ { ​ break; ​ } ​ for (i = 0; i < FIX_SCOPE_NUM; i++) ​ { ​ s = G_TP_FIXED_SCOPE[i]; ​ if ( 0 == strncmp(s, t, len) ​ && ('/' == *(s + len) || '\0' == *(s + len)) ) ​ { ​ flag = 0; ​ break; ​ } ​ } ​ if (0 == flag) ​ { ​ break; ​ } ​ for (node = g_scope_list; node; node = node->next) ​ { ​ s = (char*)node->data; ​ if (0 == strncmp(s, t, len) && ('/' == *(s + len) || '\0' == *(s + len))) ​ { ​ flag = 0; ​ break; ​ } ​ } ​ if ( 0 == flag ) ​ { ​ break; ​ } ​ if (NULL != e) ​ { ​ t = strchr(e, ' '); ​ } ​ else ​ { ​ t = NULL; ​ } ​ if (!t) ​ { ​ break; ​ } } return flag; } LOCAL S32 soap_out_scopes_item(ONVIF_BUF *xml_buf) { SOAP_NODE *node = NULL; char scope_string[FIX_SCOPE_NUM * LEN_INFO + FIX_SCOPE_NUM] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } /* wsdd:Scopes begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Scopes", NULL)); snprintf(scope_string, sizeof(scope_string), "%s %s %s %s %s", ​ G_TP_FIXED_SCOPE[0], G_TP_FIXED_SCOPE[1], G_TP_FIXED_SCOPE[2], G_TP_FIXED_SCOPE[3], G_TP_FIXED_SCOPE[4]); SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, scope_string, strlen(scope_string))); for (node = g_scope_list; node; node = node->next) { ​ if (node->data != NULL) ​ { ​ SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, " ", 1)); ​ SOAP_IF_FAIL_RET(onvif_buf_append_string(xml_buf, (char *)node->data, strlen((char *)node->data))); ​ } } /* wsdd:Scopes end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Scopes")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_hello_type() \* 函数描述: 组装wsdd:Hello element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* data -- 为空 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_hello_type(ONVIF_BUF *xml_buf, void *data) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:Hello begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:Hello", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ​ ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:Hello")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_probe_match_type() \* 函数描述: 组装wsdd:ProbeMatch element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_match_type(ONVIF_BUF *xml_buf) { S32 len = 0; char tmp_str[LEN_INFO] = {0}; char ip_str[LEN_IP] = {0}; char mac_str[LEN_MAC] = {0}; if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(onvif_get_ip_str(ip_str, LEN_IP)); SOAP_IF_FAIL_RET(onvif_get_mac_str(mac_str, LEN_MAC, FALSE)); /* wsdd:ProbeMatch begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatch", NULL)); /* wsa:EndpointReference */ len = snprintf(tmp_str, sizeof(tmp_str), "uuid:%s%s", UUID_PREFIX_FMAT, mac_str); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_out_wsa_endpoint_reference(xml_buf, "wsa:EndpointReference", tmp_str)); /* wsdd:Types */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:Types", G_TYPES_VAL_STR, NULL)); /* wsdd:Scopes */ SOAP_IF_FAIL_RET(soap_out_scopes_item(xml_buf)); /* wsdd:XAddrs */ len = snprintf(tmp_str, sizeof(tmp_str), "http://%s:%d/onvif/device_service", ​ ip_str, ONVIF_PORT_FOR_MAIN); if (len < 0 || len >= sizeof(tmp_str)) { ​ return ERROR; } SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:XAddrs", tmp_str, NULL)); /* wsdd:MetadataVersion */ SOAP_IF_FAIL_RET(soap_element(xml_buf, "wsdd:MetadataVersion", "1", NULL)); /* wsdd:ProbeMatch end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatch")); return OK; } /****************************************************************************** \* 函数名称: soap_out_wsdd_probe_matches() \* 函数描述: 组装wsdd:ProbeMatches element内容 \* 输 入: xml_buf -- 存放输出xml字串的内存地址 \* data -- 为空 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_out_wsdd_probe_matches(ONVIF_BUF *xml_buf, void *data) { if (xml_buf == NULL) { ​ ONVIF_WARN("xml_buf == NULL."); ​ return ERROR; } /* wsdd:ProbeMatches begin */ SOAP_IF_FAIL_RET(soap_element_begin_out(xml_buf, "wsdd:ProbeMatches", NULL)); /* wsdd:ProbeMatch */ SOAP_IF_FAIL_RET(soap_out_wsdd_probe_match_type(xml_buf)); /* wsdd:ProbeMatches end */ SOAP_IF_FAIL_RET(soap_element_end_out(xml_buf, "wsdd:ProbeMatches")); return OK; } /********************************************************************************* \* Soap wsdd req parse *********************************************************************************/ /****************************************************************************** \* 函数名称: soap_wsdd_probe_req_parse() \* 函数描述: 解析发现报文的具体内容 \* 输 入: soap -- soap结构体 \* req_buf -- 请求内容 \* buf_len -- 请求内容长度 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_req_parse(SOAP_CONTEXT *soap, char **req_buf, S32 buf_len) { S32 ch = 0; char charbuf[LEN_INFO] = {0}; char *str_start = NULL; char **p = req_buf; if (soap == NULL) { ​ ONVIF_TRACE("soap == NULL."); ​ return ERROR; } if (req_buf == NULL || *req_buf == NULL || buf_len <= 0) { ​ ONVIF_TRACE("no request context to parse."); ​ return OK; } str_start = *req_buf; while (((*p) - str_start) < buf_len) { ​ if ((ch = soap_get_tag(str_start, buf_len, p, charbuf, sizeof(charbuf))) == EOF) ​ { ​ goto error_parse; ​ } ​ if (charbuf[0] == '/') ​ { ​ continue; ​ } ​ if (TRUE == soap_match_tag(charbuf, "Types")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->types, sizeof(soap->types))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("Types: %s", soap->types); ​ } ​ if (TRUE == soap_match_tag(charbuf, "Scopes")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->scopes_item, sizeof(soap->scopes_item))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("Scopes: %s", soap->scopes_item); ​ } ​ if (TRUE == soap_match_tag(charbuf, "MatchBy")) ​ { ​ if (OK != soap_parse_element_value(str_start, buf_len, p, ch, ​ soap->scopes_matchby, sizeof(soap->scopes_matchby))) ​ { ​ goto error_parse; ​ } ​ ONVIF_TRACE("MatchBy: %s", soap->scopes_matchby); ​ } } return OK; error_parse: return ERROR; } /****************************************************************************** \* 函数名称: soap_wsdd_hello() \* 函数描述: 发送onvif Hello报文 \* 输 入: soap -- soap结构体 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ S32 soap_wsdd_hello(SOAP_CONTEXT *soap) { if (soap == NULL) { ​ ONVIF_TRACE("soap == NULL."); ​ return ERROR; } SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_hello_type), soap, NULL)); return onvif_send_udp_packet(soap->sock, inet_addr(ONVIF_MCAST_IP), htons(ONVIF_PORT_FOR_DISCV), ​ soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); } /****************************************************************************** \* 函数名称: soap_wsdd_probe_handle() \* 函数描述: 发现请求的处理函数 \* 输 入: soap -- soap结构体 \* 输 出: N/A \* 返 回 值: ERROR/OK ******************************************************************************/ LOCAL S32 soap_wsdd_probe_handle(SOAP_CONTEXT *soap) { S32 request_len = 0; BOOL match = FALSE; char *xml_buf = NULL; char **xml_str = NULL; char ifname[LEN_IFNAME] = {0}; S32 scopes_matchby_flag = 0; if (soap == NULL) { ​ ONVIF_WARN("soap == NULL."); ​ return ERROR; } if (onvif_get_discv_mode() == DIS_MODE_NO) { ​ ONVIF_DEBUG("discover mode is non-discoverable."); ​ return OK; } if (soap->request_begin != NULL && soap->request_end != NULL) { ​ request_len = soap->request_end - soap->request_begin; ​ if (request_len < 0) ​ { ​ ONVIF_WARN("request_len < 0."); ​ return ERROR; ​ } ​ xml_buf = soap->request_begin; ​ xml_str = (char **)&xml_buf; ​ if (strstr(xml_buf, "<d:Scopes MatchBy=\"http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc3986\"></d:Scopes>") != NULL) ​ { ​ scopes_matchby_flag = 1; ​ } } else { ​ ONVIF_ERROR("soap request content is NULL."); ​ return ERROR; } if (OK != soap_wsdd_probe_req_parse(soap, xml_str, request_len)) { ​ return ERROR; } if (soap->types[0] != '\0') { ​ if(0 == strcmp("tds:Device", soap->types) || ​ 0 == strcmp("tdn:NetworkVideoTransmitter", soap->types) || ​ 0 == strcmp("dn:NetworkVideoTransmitter", soap->types)) ​ { ​ match = TRUE; ​ } ​ else if(0 == strcmp("dn:NetworkVideoRecorder", soap->types) || ​ 0 == strcmp("tdn:NetworkVideoRecorder", soap->types)) ​ { ​ return OK; ​ } } else { ​ match = TRUE; } if (match) { ​ if (onvif_get_netdevice_name(soap->ifindex, ifname, LEN_IFNAME) == 0 && ​ strncmp(ifname, IFNAME_AP, LEN_IFNAME) != 0) ​ { ​ /* 通知IPCD进行probe IP的跟随修改 */ ​ NSD_SEND(NSD_PROBE_IP, (U8*)(&soap->in_ip), sizeof(soap->in_ip)); ​ } } if (soap->scopes_item[0] != '\0') { ​ if (soap->scopes_matchby[0] != '\0') ​ { ​ if (soap->has_header) ​ { ​ snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), ​ "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); ​ snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), ​ "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); ​ } ​ soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", ​ NULL, "the matching rule specified is not supported"); ​ soap->error = SOAP_FAULT; ​ return ERROR; ​ } ​ if (0 != soap_check_scope(soap->scopes_item)) ​ { ​ return ERROR; ​ } } if (scopes_matchby_flag == 1) { ​ if (soap->has_header) ​ { ​ snprintf(soap->header.wsa_action, sizeof(soap->header.wsa_action), ​ "http://schemas.xmlsoap.org/ws/2005/04/discovery/fault"); ​ snprintf(soap->header.wsa_to, sizeof(soap->header.wsa_to), ​ "http://schemas.xmlsoap.org/ws/2005/04/addressing/role/anonymous"); ​ } ​ soap_fault(soap, "SOAP-ENV:Sender", "wsdd:MatchingRuleNotSupported", ​ NULL, "the matching rule specified is not supported"); ​ soap->error = SOAP_FAULT; ​ return ERROR; } //SOAP_IF_FAIL_RET(soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL)); //return onvif_send_udp_packet(soap->sock, soap->in_ip, htons(ONVIF_PORT_FOR_DISCV), // soap->xml_buf.start, (soap->xml_buf.last - soap->xml_buf.start)); if (soap->header.wsa_relates_to[0] == '\0') { ​ snprintf(soap->header.wsa_relates_to, sizeof(soap->header.wsa_relates_to), ​ "%s", soap->header.wsa_msg_id); } if (soap->header.wsa_action[0] != '\0') { ​ onvif_str_append_chr(soap->header.wsa_action, sizeof(soap->header.wsa_action), "Matches"); } return soap_generate_xml((p_out_fun)(soap_out_wsdd_probe_matches), soap, NULL); } /********************************************************************************* \* Soap wsdd Handles init *********************************************************************************/ void soap_wsdd_handle_init() { soap_tag_handle_add("Probe", soap_wsdd_probe_handle); }——这部分是onvif模块剩下的部分代码,当前onvif模块仅支持http不支持https,需要提供onvif over tls支持,能够兼容切换非tls和tls client连接,端口选择443,请你分析下目前已有条件还有接下来需要做的所有修改,详细阐述并给出代码
10-08
【RIS 辅助的 THz 混合场波束斜视下的信道估计与定位】在混合场波束斜视效应下,利用太赫兹超大可重构智能表面感知用户信道与位置(Matlab代码实现)内容概要:本文围绕“IS 辅助的 THz 混合场波束斜视下的信道估计与定位”展开,重点研究在太赫兹(THz)通信系统中,由于混合近场与远场共存导致的波束斜视效应下,如何利用超大可重构智能表面(RIS)实现对用户信道状态信息和位置的联合感知与精确估计。文中提出了一种基于RIS调控的信道参数估计算法,通过优化RIS相移矩阵提升信道分辨率,并结合信号到达角(AoA)、到达时间(ToA)等信息实现高精度定位。该方法在Matlab平台上进行了仿真验证,复现了SCI一区论文的核心成果,展示了其在下一代高频通信系统中的应用潜力。; 适合人群:具备通信工程、信号处理或电子信息相关背景,熟悉Matlab仿真,从事太赫兹通信、智能反射面或无线定位方向研究的研究生、科研人员及工程师。; 使用场景及目标:① 理解太赫兹通信中混合场域波束斜视问题的成因与影响;② 掌握基于RIS的信道估计与用户定位联合实现的技术路径;③ 学习并复现高水平SCI论文中的算法设计与仿真方法,支撑学术研究或工程原型开发; 阅读建议:此资源以Matlab代码实现为核心,强调理论与实践结合,建议读者在理解波束成形、信道建模和参数估计算法的基础上,动手运行和调试代码,深入掌握RIS在高频通信感知一体化中的关键技术细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值