Hardwired TCP/IP Chapter 13: W55MH32 UPnP Port Forwarding Example
Hardwired TCP/IP Chapter 13: W55MH32 UPnP Port Forwarding Example

Hardwired TCP/IP Chapter 13: W55MH32 UPnP Port Forwarding Example
In this article, we will provide a detailed explanation on how to implement the UPnP protocol on the W55MH32 chip. By using the TOE engine of W55MH32, we only need to perform simple socket programming and register reading and writing, and we can easily implement Ethernet applications. Next, through practical examples, we will explain to you how to use the TOE engine to implement the port forwarding function of the UPnP protocol.
Other network protocols used in this example, such as DHCP, please refer to the relevant chapters. Regarding the initialization process of W55MH32, please refer to the Network Install chapter. We will not elaborate on this here.
1 Introduction to the UPnP Protocol
The UPnP (Universal Plug and Play) protocol is a network protocol that enables devices in a local area network to achieve automatic discovery and communication. Its port forwarding function is provided by the IGD Profile, allowing local network devices to dynamically request the router to open specified ports for them, so as to enable external devices to access internal services. This feature eliminates the complexity of manual port forwarding configuration, and is particularly suitable for scenarios that require penetrating the NAT (Network Address Translation) environment.
IGD (Internet Gateway Device) is a part of the UPnP (Universal Plug and Play) protocol. It is mainly used to manage the services and resources of gateway devices (such as routers) in the network. The IGD extension defines a set of standard interfaces, allowing local network devices to communicate with gateway devices, and dynamically configuring network settings, such as port forwarding, bandwidth management, and connection status queries, etc.
2 Characteristics of the UPnP Protocol
- Automated configuration: No need for manual settings by users, reducing the risk of configuration errors.
- Dynamic and flexible: Port mapping rules can be added or deleted dynamically according to requirements.
- Device-friendly: Supports plug-and-play, simplifying the process of device networking and deployment.
- Cross-device compatibility: UPnP is based on standardized protocols and widely supports various devices and platforms.
3 UPnP application scenarios
Through the UPnP port forwarding function, we can utilize the W55MH32 to achieve the following functions:
- Remote access: External requests can be forwarded to local network devices (such as NAS, surveillance cameras), enabling remote access to internal devices from outside.
- Remote control: External devices can be remotely controlled by the UPnP converted ports, allowing for remote control of local network internal devices (smart door locks, lighting controllers).
4 The workflow of setting up port forwarding with UPnP
1. Device discovery: W55MH32 sends multicast requests (HTTP M-SEARCH messages) via SSDP (Simple Service Discovery Protocol) to the local network to search for gateway devices that support IGD.
2. Obtain service description: W55MH32 accesses the gateway device (router) to obtain the service description file to understand the supported services and interfaces.
3. Subscribe to IGD events: Through event subscription, W55MH32 can receive real-time notifications without actively polling.
4. Call service interface: Using UPnP's SOAT messages, W55MH32 calls the port mapping interface provided by IGD.
5. Data interaction test: Externally, communication is conducted by accessing the mapped port and the router address with devices within the local network.
5 Message Explanation
Equipment search
As we mentioned earlier, the SSDP protocol is used during device search. SSDP (Simple Service Discovery Protocol) is a key protocol within the UPnP protocol and is used for device discovery and service publication. It broadcasts and receives messages in the form of HTTP over UDP within the local network, using the multicast address 239.255.255.250 and port 1900.
The SSDP messages are mainly divided into the following categories:
- NOTIFY message (device-initiated broadcast notification): Used by the device to inform the network of its presence or offline status.
- M-SEARCH message (client-initiated search): The client sends a search request to discover the device or service.
- HTTP/1.1 response message (device's response to M-SEARCH): The device's response to the search request, providing the location of the device description file and service information.
SSDP messages are based on the HTTP protocol and have a fixed format, mainly including the following fields:
- HOST: Target address and port, fixed as 239.255.255.250:1900.
- MAN: Used for identifying the search message, fixed as "ssdp:discover" (only used in M-SEARCH).
- MX: Maximum response time, specifies the time within which the device responds (unit: seconds).
- ST: Search target, identifies the type of device or service to be found.
- NT: Notification type, indicates the type of device or service (used in NOTIFY message).
- USN: Unique service name, the unique identifier of the device or service.
- LOCATION: URL of the device description file, containing detailed information of the device.
- CACHE-CONTROL: The caching time of the device information, indicating the validity period of the information.
M-SEARCH request message example:
M-SEARCH * HTTP/1.1
Host:239.255.255.250:1900
ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1
Man:"ssdp:discover"
MX:3
Field parsing:
M-SEARCH * HTTP/1.1: Indicates a search request.
Host: Multicast address and port.
ST: Search target type, here it is IGD device.
MX: Maximum response event, the device needs to return a response within 3 seconds.
Man: Search request type, fixed.
M-SEARCH response message example:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=60
DATE: Tue, 07 Jan 2025 06:43:49 GMT
EXT:
LOCATION: http://192.168.100.1:1900/igd.xml
SERVER: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
USN: uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9::urn:schemas-upnp-org:device:InternetGatewayDevice:1
HTTP/1.1 200 OK: This indicates a successful response.
CACHE-CONTROL: The response is valid for 60 seconds.
DATE: The timestamp of the response.
EXT: Reserved field, currently empty.
LOCATION: The URL of the device description file.
SERVER: The operating system of the device, UPnP version and device name.
ST: The search target type, which is consistent with the ST field in the request.
USN: Unique device identifier.
Obtaining the device identifier
This step will request an XML file through the HTTP GET method. Regarding the HTTP GET message and HTTP response message, there is no need to elaborate further here. Those interested can refer to the HTTP Client chapter.
Request example:
GET /igd.xml HTTP/1.1
Accept: text/xml, application/xml
User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)
Host: 192.168.100.1:1900
Connection: Keep-Alive
Cache-Control: no-cache
Pragma: no-cache
Response example:
HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Content-Length: 2580
Connection: close
Cache-control: no-cache
<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
<presentationURL>http://192.168.100.1:80 </presentationURL>
<friendlyName>Wireless N Router TL-WR886N</friendlyName>
<manufacturer>TP-LINK</manufacturer>
<manufacturerURL>http://www.tp-link.com.cn</manufacturerURL>
<modelDescription>TL-WR886N 6.0</modelDescription>
<modelName>TL-WR886N</modelName>
<modelNumber>6.0</modelNumber>
<UDN>uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9</UDN>
<UPC>123456789001</UPC>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
<serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
<controlURL>/l3f</controlURL>
<eventSubURL>/l3f</eventSubURL>
<SCPDURL>/l3f.xml</SCPDURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
<friendlyName>WAN Device</friendlyName>
<manufacturer>TP-LINK</manufacturer>
<manufacturerURL>http://www.tp-link.com.cn</manufacturerURL>
<modelDescription>WAN Device</modelDescription>
<modelName>WAN Device</modelName>
<modelNumber>1.0</modelNumber>
<modelURL></modelURL>
<serialNumber>12345678900001</serialNumber>
<UDN>uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9</UDN>
<UPC>123456789001</UPC>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANCommonInterfaceConfig</serviceId>
<controlURL>/ifc</controlURL>
<eventSubURL>/ifc</eventSubURL>
<SCPDURL>/ifc.xml</SCPDURL>
</service>
</serviceList>
<deviceList>
<device>
<deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
<friendlyName>WAN Connection Device</friendlyName>
<manufacturer>TP-LINK</manufacturer>
<manufacturerURL>http://www.tp-link.com.cn</manufacturerURL>
<modelDescription>WAN Connection Device</modelDescription>
<modelName>WAN Connection Device</modelName>
<modelNumber>1.0</modelNumber>
<modelURL></modelURL>
<serialNumber>12345678900001</serialNumber>
<UDN>uuid:8c15e41f-3d83-41c1-b35d-5D2A64377DE9</UDN>
<UPC>123456789001</UPC>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
<serviceId>urn:upnp-org:serviceId:WANIPConnection</serviceId>
<controlURL>/ipc</controlURL>
<eventSubURL>/ipc</eventSubURL>
<SCPDURL>/ipc.xml</SCPDURL>
</service>
</serviceList>
</device>
</deviceList>
</device>
</deviceList>
</device>
</root>
Subscribe to IGD events
Subscribe to IGD events via HTTP SUBSCRIBE. Example:
SUBSCRIBE /ipc HTTP/1.1
Host: 192.168.100.1:1900
USER-AGENT: Mozilla/4.0 (compatible; UPnP/1.1; Windows NT/5.1)
CALLBACK: <http://192.168.100.101:5002/>
NT: upnp:event
TIMEOUT: Second-1800
Response example:
HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Content-Length: 0
Connection: close
Cache-control: no-cache
Server: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0
Timeout: Second-1800
SID: uuid:82-2150160019
Add mapping port message
For example, if we want to map the internal port 8000 of the TCP protocol to the external port 1000, we can make an HTTP request as follows:
POST /ipc HTTP/1.1
Content-Type: text/xml; charset="utf-8"
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)
Host: 192.168.100.1:1900
Content-Length: 1131
Connection: Keep-Alive
Cache-Control: no-cache
Pragma: no-cache
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:AddPortMapping xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewRemoteHost xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">
</NewRemoteHost>
<NewExternalPort xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui2">
1000</NewExternalPort>
<NewProtocol xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">
TCP</NewProtocol>
<NewInternalPort xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui2">
8000</NewInternalPort>
<NewInternalClient xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">
192.168.100.101</NewInternalClient>
<NewEnabled xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="boolean">1</NewEnabled>
<NewPortMappingDescription xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">W5500_uPnPGetway</NewPortMappingDescription>
<NewLeaseDuration xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui4">0</NewLeaseDuration>
</m:AddPortMapping>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The descriptions of the main fields are as follows:
m: AddPortMapping: Add Port Mapping
NewExternalPort: External Port Number
NewProtocol: Protocol Type
NewInternalPort: Internal Port Number
NewInternalClient: Internal Address
Response Content:
HTTP/1.1 200 OK
Content-Type: text/xml;charset=UTF-8
Content-Length: 289
Connection: close
Cache-control: no-cache
Server: vxWorks/5.5 UPnP/1.0 TL-WR886N/6.0
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:AddPortMappingResponse xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"></u:AddPortMappingResponse></s:Body></s:Envelope>
Delete port mapping message
For example, if we want to delete the 1000 port that was previously mapped, we can make an HTTP request as follows:
POST /ipc HTTP/1.1
Content-Type: text/xml; charset="utf-8"
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping"
User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)
Host: 192.168.100.1:1900
Content-Length: 604
Connection: Keep-Alive
Cache-Control: no-cache
Pragma: no-cache
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body>
<m:DeletePortMapping xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1">
<NewRemoteHost xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string"></NewRemoteHost>
<NewExternalPort xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui2">1000</NewExternalPort>
<NewProtocol xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">TCP</NewProtocol>
</m:DeletePortMapping>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
The descriptions of the main fields are as follows:
m: DeletePortMapping: Delete Port Mapping
NewExternalPort: External Port Number
NewProtocol: Protocol Type
NewRemoteHost: External Access Source, can be empty
6 The implementation process
In this routine, we have implemented the functions of controlling the LED light switch via serial port, obtaining and setting network address information, conducting TCP and UDP loopback data tests, and adding and deleting mapping ports using UPnP.
Note: The test instance requires the W55MH32 to be connected to a router that supports UPnP port forwarding.
Step 1: Set the size of the Ethernet buffer
1. static uint8_t tx_size[_WIZCHIP_SOCK_NUM_] = {4, 4, 2, 1, 1, 1, 1, 2};
2. static uint8_t rx_size[_WIZCHIP_SOCK_NUM_] = {4, 4, 2, 1, 1, 1, 1, 2};
1. /* socket rx and tx buff init */
2. wizchip_init(tx_size, rx_size);
3.
Here, we set the receive and transmit buffers for sockets 0 to 7 to 4KB, 4KB, 2KB, 1KB, 1KB, 1KB, 1KB, and 2KB respectively.
Socket 0 is used for UPnP protocol processing, socket 1 is used for TCP and UDP loopback processing, and socket 2 is used for listening for IGD events.
Step 2: Registration of LED Control Function
1. UserLED_Control_Init(set_user_led_status);
2.
The set_user_led_status() function is a function for controlling the LED. The detailed content is as follows:
1. void set_user_led_status(uint8_t val)
2. {
3. if (val)
4. {
5. GPIO_SetBits(GPIOD, GPIO_Pin_14);
6. }
7. else
8. {
9. GPIO_ResetBits(GPIOD, GPIO_Pin_14);
10. }
11. }
Step 3: Search for UPnP devices
1. do
2. {
3. printf("Send SSDP.. \r\n");
4. } while (SSDPProcess(SOCKET_ID) != 0); // SSDP Search discovery
5.
1. /**< SSDP Header */
2. unsigned char SSDP[] = "\
3. M-SEARCH * HTTP/1.1\r\n\
4. Host:239.255.255.250:1900\r\n\
5. ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n\
6. Man:\"ssdp:discover\"\r\n\
7. MX:3\r\n\
8. \r\n\
9. ";
10.
11. /**
12. * @brief This function processes the SSDP message.
13. * @return 0: success, -1: reply packet timeout, 1: received SSDP parse error
14. */
15. signed char SSDPProcess(SOCKET sockfd)
16. {
17. char ret_value = 0;
18. long endTime = 0;
19. unsigned char mcast_addr[4] = {239, 255, 255, 250};
20. // unsigned char_t_t mcast_mac[6] = {0x28, 0x2C, 0xB2, 0xE9, 0x42, 0xD6};
21. unsigned char recv_addr[4];
22. unsigned short recv_port;
23.
24. // UDP Socket Open
25. close(sockfd);
26. socket(sockfd, Sn_MR_UDP, PORT_SSDP, 0); /*Initialize socket for socket 0*/
27. while (getSn_SR(sockfd) != SOCK_UDP);
28.
29. #ifdef UPNP_DEBUG
30. printf("%s\r\n", SSDP);
31. #endif
32.
33. // Send SSDP
34. if (sendto(sockfd, SSDP, strlen((char *)SSDP), mcast_addr, 1900) <= 0)
35. printf("SSDP Send error!!!!!!!\r\n");
36.
37. // Receive Reply
38. memset(recv_buffer, '\0', RECV_BUFFER_SIZE);
39. endTime = my_time + 3;
40. while (recvfrom(sockfd, (unsigned char *)recv_buffer, RECV_BUFFER_SIZE, recv_addr, &recv_port) <= 0 && my_time < endTime); // Check Receive Buffer
41. if (my_time >= endTime)
42. { // Check Timeout
43. close(sockfd);
44. return -1;
45. }
46.
47. // UDP Socket Close
48. close(sockfd);
49.
50. #ifdef UPNP_DEBUG
51. printf("\r\nReceiveData\r\n%s\r\n", recv_buffer);
52. #endif
53.
54. // Parse SSDP Message
55. if ((ret_value = parseSSDP(recv_buffer)) == 0)
56. UPnP_Step = 1;
57. return ret_value;
58. }
In this function, the main operation is to search for IGD devices using the SSDP protocol, and to send messages in a manner consistent with what we have previously described.
Step 4: Obtain the description of the IGD device
1. if (GetDescriptionProcess(SOCKET_ID) == 0) // GET IGD description
2. {
3. printf("GetDescription Success!!\r\n");
4. }
5. else
6. {
7. printf("GetDescription Fail!!\r\n");
8. }
9.
1. * @brief This function gets the description message from IGD(Internet Gateway Device).
2. * @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error
3. */
4. signed char GetDescriptionProcess(
5. SOCKET sockfd /**< a socket number. */
6. )
7. {
8. char ret_value = 0;
9. long endTime = 0;
10. unsigned long ipaddr;
11. unsigned short port;
12.
13. // Check UPnP Step
14. if (UPnP_Step < 1) return -2;
15.
16. // Make HTTP GET Header
17. memset(send_buffer, '\0', SEND_BUFFER_SIZE);
18. MakeGETHeader(send_buffer);
19.
20. #ifdef UPNP_DEBUG
21. printf("%s\r\n", send_buffer);
22. #endif
23.
24. ipaddr = inet_addr((unsigned char *)descIP);
25. ipaddr = swapl(ipaddr);
26. port = ATOI(descPORT, 10);
27.
28. // Connect to IGD(Internet Gateway Device)
29. close(sockfd);
30. socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
31. while (getSn_SR(sockfd) != SOCK_INIT)
32. {
33. delay_ms(100);
34. }
35.
36. if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
37. printf("TCP Socket Error!!\r\n");
38.
39. // Send Get Discription Message
40. while ((getSn_SR(sockfd) != SOCK_ESTABLISHED));
41. send(sockfd, (void *)send_buffer, strlen(send_buffer));
42.
43. // Receive Reply
44. memset(recv_buffer, '\0', RECV_BUFFER_SIZE);
45. delay_ms(500);
46. endTime = my_time + 3;
47. while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
48. if (my_time >= endTime)
49. { // Check Timeout
50. close(sockfd);
51. return -1;
52. }
53.
54. // TCP Socket Close
55. close(sockfd);
56.
57. #ifdef UPNP_DEBUG
58. printf("\r\nReceiveData\r\n%s\r\n", recv_buffer);
59. #endif
60.
61. // Parse Discription Message
62. if ((ret_value = parseDescription(recv_buffer)) == 0) UPnP_Step = 2;
63. return ret_value;
64. }
The request message is packaged through the MakeGETHeader() function. The specific message is as follows:
1. /**
2. * @brief This function makes the HTTP GET header.
3. * @param dest:Target string pointer
4. * @return none
5. */
6. void MakeGETHeader(char *dest)
7. {
8. char local_port[6] = {'\0'};
9. strcat(dest, "GET ");
10. strcat(dest, descLOCATION);
11. strcat(dest, " HTTP/1.1\r\n");
12. strcat(dest, "Accept: text/xml, application/xml\r\n");
13. strcat(dest, "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n");
14. strcat(dest, "Host: ");
15. strcat(dest, descIP);
16. sprintf(local_port, ":%s", descPORT);
17. strcat(dest, local_port);
18. strcat(dest, "\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n");
19. }
Then, the received content is parsed through the `parseDescription()` function. If the device description does not support the WANIPConnection service, it indicates that port mapping is not supported, and an error is returned.
The content of the `parseDescription()` function is as follows:
1. /**
2. * @brief This function parses the received description message from IGD(Internet Gateway Device).
3. * @return 0: success, 1: received xml parse error
4. */
5. signed char parseDescription(
6. const char *xml /**< string for parse */
7. )
8. {
9. const char controlURL_[] = "<controlURL>";
10. const char eventSubURL_[] = "<eventSubURL>";
11. char *URL_start = 0, *URL_end = 0;
12.
13. if (parseHTTP(xml) != 0) return 1;
14. //printf("\r\n%s\r\n", xml);
15. // Find Control URL("/etc/linuxigd/gateconnSCPD.ctl")
16. if ((URL_start = strstr(xml, "urn:schemas-upnp-org:service:WANIPConnection:1")) == NULL) return 1;
17. if ((URL_start = strstr(URL_start, controlURL_)) == NULL) return 1;
18. if ((URL_end = strstr(URL_start, "</controlURL>")) == NULL) return 1;
19.
20. strncpy(controlURL, URL_start + strlen(controlURL_), URL_end - URL_start - strlen(controlURL_));
21.
22. // Find Eventing Subscription URL("/etc/linuxigd/gateconnSCPD.evt")
23. if ((URL_start = strstr(xml, "urn:schemas-upnp-org:service:WANIPConnection:1")) == NULL) return 1;
24. if ((URL_start = strstr(URL_start, eventSubURL_)) == NULL) return 1;
25. if ((URL_end = strstr(URL_start, "</eventSubURL>")) == NULL) return 1;
26.
27. strncpy(eventSubURL, URL_start + strlen(eventSubURL_), URL_end - URL_start - strlen(eventSubURL_));
28.
29. return 0;
30. }
Step 5: Subscribe to IGD events
1. if (SetEventing(SOCKET_ID) == 0) // Subscribes IGD event messages
2. {
3. printf("SetEventing Success!!\r\n");
4. }
5. else
6. {
7. printf("SetEventing Fail!!\r\n");
8. }
The content of the SetEventing() function is as follows:
1. /**
2. * @brief This function subscribes to the eventing message from IGD(Internet Gateway Device).
3. * @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout
4. */
5. signed char SetEventing(
6. SOCKET sockfd /**< a socket number. */
7. )
8. {
9. long endTime = 0;
10. unsigned long ipaddr;
11. unsigned short port;
12.
13. // Check UPnP Step
14. if (UPnP_Step < 2) return -2;
15.
16. // Make Subscription message
17. memset(send_buffer, '\0', SEND_BUFFER_SIZE);
18. MakeSubscribe(send_buffer, PORT_UPNP_EVENTING);
19.
20. #ifdef UPNP_DEBUG
21. printf("%s\r\n", send_buffer);
22. #endif
24. ipaddr = inet_addr((unsigned char *)descIP);
25. ipaddr = swapl(ipaddr);
26. port = ATOI(descPORT, 10);
27.
28. // Connect to IGD(Internet Gateway Device)
29. close(sockfd);
30. socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
31. while (getSn_SR(sockfd) != SOCK_INIT)
32. {
33. delay_ms(100);
34. }
35.
36. if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
37. printf("TCP Socket Error!!\r\n");
38.
39. // Send Get Discription Message
40. while ((getSn_SR(sockfd) != SOCK_ESTABLISHED));
41. send(sockfd, (void *)send_buffer, strlen(send_buffer));
42.
43. // Receive Reply
44. memset(recv_buffer, '\0', RECV_BUFFER_SIZE);
45. delay_ms(500);
46. endTime = my_time + 3;
47. while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
48. if (my_time >= endTime)
49. { // Check Timeout
50. close(sockfd);
51. return -1;
52. }
53.
54. // TCP Socket Close
55. close(sockfd);
56.
57. #ifdef UPNP_DEBUG
58. printf("\r\nReceiveData\r\n%s\r\n", recv_buffer);
59. #endif
60.
61. return parseHTTP(recv_buffer);
62. }
63.
The request message is packaged through the MakeSubscribe() function. The specific message is as follows:
1. /**
2. * @brief This function makes the Subscription message.
3. * @param dest:Target string pointer
4. * @param listen_port:Listen port
5. * @return none
6. */
7. void MakeSubscribe(char *dest, const unsigned int listen_port)
8. {
9. char local_port[6] = {'\0'}, ipaddr[16] = {'\0'};
10. unsigned char ip[4];
11. strcat(dest, "SUBSCRIBE ");
12. strcat(dest, eventSubURL);
13. strcat(dest, " HTTP/1.1\r\n");
14. strcat(dest, "Host: ");
15. strcat(dest, descIP);
16. sprintf(local_port, ":%s", descPORT);
17. strcat(dest, local_port);
18. strcat(dest, "\r\nUSER-AGENT: Mozilla/4.0 (compatible; UPnP/1.1; Windows NT/5.1)\r\n");
19. strcat(dest, "CALLBACK: <http://");
20. getSIPR(ip);
21. sprintf(ipaddr, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
22. strcat(dest, ipaddr);
23. sprintf(local_port, ":%d/>", listen_port);
24. strcat(dest, local_port);
25. strcat(dest, "\r\nNT: upnp:event\r\nTIMEOUT: Second-1800\r\n\r\n");
26. }
27.
Finally, the HTTP response message is parsed through the parseHTTP() function to determine whether the subscription was successful.
The parseHTTP() function is as follows:
1. /*-----String Parse Functions-----*/
2.
3. /**
4. * @brief This function parses the HTTP header.
5. * @return 0: success, 1: received xml parse error
6. */
7. signed char parseHTTP(
8. const char *xml /**< string for parse */
9. )
10. {
11. char *loc = 0;
12. if (strstr(xml, "200 OK") != NULL)
13. return 0;
14. else
15. {
16. loc = strstr(xml, "\r\n");
17. memset(content, '\0', CONT_BUFFER_SIZE);
18. strncpy(content, xml, loc - xml);
19. printf("\r\nHTTP Error:\r\n%s\r\n\r\n", content);
20. return 1;
21. }
22. }
Step 6: Execute the UPnP main program
1. Main_Menu(SOCKET_ID, SOCKET_ID + 1, SOCKET_ID + 2, ethernet_buf, tcps_port, udps_port); // Main menu
2.
1. /**
2. * @brief Display/Manage a Menu on HyperTerminal Window
3. * @param sn: use for SSDP; sn2: use for run tcp/udp loopback; sn3: use for listenes IGD event message
4. * @param buf: use for tcp/udp loopback rx/tx buff; tcps_port: use for tcp loopback listen; udps_port: use for udp loopback receive
5. * @return none
6. */
7. void Main_Menu(uint8_t sn, uint8_t sn2, uint8_t sn3, uint8_t *buf, uint16_t tcps_port, uint16_t udps_port)
8. {
9. static char choice[3];
10. static char msg[256], ipaddr[12], protocol[4];
11. static unsigned short ret, external_port, internal_port;
12. static uint8_t bTreat;
13. static uint8_t Sip[4];
14.
15. while (1)
16. {
17. /* Display Menu on HyperTerminal Window */
18. bTreat = RESET;
19. printf("\r\n====================== WIZnet Chip Control Point ===================\r\n");
20. printf("This Application is basic example of UART interface with\r\n");
21. printf("Windows Hyper Terminal. \r\n");
22. printf("\r\n==========================================================\r\n");
23. printf(" APPLICATION MENU :\r\n");
24. printf("\r\n==========================================================\r\n\n");
25. printf(" 1 - Set LED on \r\n");
26. printf(" 2 - Set LED off \r\n");
27. printf(" 3 - Show network setting\r\n");
28. printf(" 4 - Set network setting\r\n");
29. printf(" 5 - Run TCP Loopback\r\n");
30. printf(" 6 - Run UDP Loopback\r\n");
31. printf(" 7 - UPnP PortForwarding: AddPort\r\n");
32. printf(" 8 - UPnP PortForwarding: DeletePort\r\n");
33.
34. printf("Enter your choice : ");
35. memset(choice, 0, sizeof(choice));
36. scanf("%s", choice);
37. printf("%c\r\n", choice[0]);
Here, a user option menu will be executed. Options 1 and 2 control the LED switch, options 3 and 4 print and set the network address information, option 5 runs a TCP loopback test program (the loopback test program can be referred to in the TCP Server section), option 6 runs a UDP loopback test program (the loopback test program can be referred to in the UDP section). Option 7 adds an UPnP port mapping table, and option 8 deletes an UPnP port mapping table. Here, we mainly explain options 7 and 8 related to UPnP.
Step 7: Add an UPnP Port Mapping Table
The code is as follows:
1. if (choice[0] == '7')
2. {
3. bTreat = SET;
4.
5. printf("\r\nType a Protocol(TCP/UDP) : ");
6. memset(msg, 0, sizeof(msg));
7. scanf("%s", msg);
8. printf("%s\r\n", msg);
9.strncpy(protocol, msg, 3);
10.protocol[3] = '\0';
11.
12.printf("\r\nType a External Port Number : ");
13.memset(msg, 0, sizeof(msg));
14.scanf("%s", msg);
15.printf("%s\r\n", msg);
16.external_port = ATOI(msg, 10);
17.
18.printf("\r\nType a Internal Port Number : ");
19.memset(msg, 0, sizeof(msg));
20.scanf("%s", msg);
21.printf("%s\r\n", msg);
22.internal_port = ATOI(msg, 10);
23.if(strcmp(protocol,"tcp") || strcmp(protocol,"TCP"))
24.tcps_port = internal_port;
25. else
26. udps_port = internal_port;
27. close(sn2);
28. // Try to Add Port Action
29. getSIPR(Sip);
30. sprintf(ipaddr, "%d.%d.%d.%d", Sip[0], Sip[1], Sip[2], Sip[3]);
31. if ((ret = AddPortProcess(sn, protocol, external_port, ipaddr, internal_port, "W5500_uPnPGetway")) == 0)
32. printf("AddPort Success!!\r\n");
33else
34. printf("AddPort Error Code is %d\r\n", ret);
35. }
Here, we need the protocol type of the external input port mapping (TCP or UDP), as well as the external port number and the internal port number. After the input is completed, the port number in option 5 or option 6 will be replaced by the input internal port number, and then the port mapping addition processing will be executed through the AddPortProcess() function. The content of the AddPortProcess() function is as follows:
1. /**
2. * @brief This function processes the add port to IGD(Internet Gateway Device).
3. * @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error, other: UPnP error code
4. */
5. signed short AddPortProcess(
6. SOCKET sockfd, /**< a socket number. */
7. const char *protocol, /**< a procotol name. "TCP" or "UDP" */
8. const unsigned int extertnal_port, /**< an external port number. */
9. const char *internal_ip, /**< an internal ip address. */
10. const unsigned int internal_port, /**< an internal port number. */
11. const char *description /**< a description of this portforward. */
12. )
13. {
14. short len = 0;
15. long endTime = 0;
16. unsigned long ipaddr;
17. unsigned short port;
+ 3;18.
19. // Check UPnP Step
20. if (UPnP_Step < 2) return -2;
21.
22. // Make "Add Port" XML(SOAP)
23. memset(content, '\0', CONT_BUFFER_SIZE);
24. MakeSOAPAddControl(content, protocol, extertnal_port, internal_ip, internal_port, description);
25.
26. // Make HTTP POST Header
27. memset(send_buffer, '\0', SEND_BUFFER_SIZE);
28. len = strlen(content);
29. MakePOSTHeader(send_buffer, len, ADD_PORT);
30. strcat(send_buffer, content);
31.
32. //#ifdef UPNP_DEBUG
33. printf("%s\r\n", send_buffer);
34. //#endif
35.
36. ipaddr = inet_addr((unsigned char *)descIP);
37. ipaddr = swapl(ipaddr);
38. port = ATOI(descPORT, 10);
39.
40. // Connect to IGD(Internet Gateway Device)
41. socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
42. while (getSn_SR(sockfd) != SOCK_INIT);
43.
44. if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
45. printf("TCP Socket Error!!\r\n");
46.
47. // Send "Delete Port" Message
48. while (getSn_SR(sockfd) != SOCK_ESTABLISHED);
49. send(sockfd, (void *)send_buffer, strlen(send_buffer));
50.
51. // Receive Reply
52. memset(recv_buffer, '\0', RECV_BUFFER_SIZE);
53. delay_ms(500);
54. endTime = my_time
55. while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
56. if (my_time >= endTime)
57. { // Check Timeout
58. close(sockfd);
59. return -1;
60. }
61.
62. // TCP Socket Close
63. close(sockfd);
64.
65. //#ifdef UPNP_DEBUG
66. printf("\r\nReceiveData\r\n%s\r\n", recv_buffer);
67. //#endif
68.
69. // Parse Replied Message
70. return parseAddPort(recv_buffer);
71. }
72.
1. /**< SOAP header & tail */
2. const char soap_start[] =
3. "\
4. <?xml version=\"1.0\"?>\r\n\
5. <SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><SOAP-ENV:Body>\
6. ";
7.
8. const char soap_end[] =
9. "\
10. </SOAP-ENV:Body></SOAP-ENV:Envelope>\r\n\
11. ";
12.
13. /**< Delete Port Mapping */
14. const char DeletePortMapping_[] = "<m:DeletePortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">";
15. const char _DeletePortMapping[] = "</m:DeletePortMapping>";
16.
17. /**< New Remote Host */
18. const char NewRemoteHost_[] = "<NewRemoteHost xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">";
19. const char _NewRemoteHost[] = "</NewRemoteHost>";
20.
21. /**< New External Port */
22. const char NewExternalPort_[] = "<NewExternalPort xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui2\">";
23. const char _NewExternalPort[] = "</NewExternalPort>";
24.
25. /**< New Protocol */
26. const char NewProtocol_[] = "<NewProtocol xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">";
27. const char _NewProtocol[] = "</NewProtocol>";
28.
29. /**< Add Port Mapping */
30. const char AddPortMapping_[] = "<m:AddPortMapping xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:1\">";
31. const char _AddPortMapping[] = "</m:AddPortMapping>";
32.
33. /**< New Internal Port */
34. const char NewInternalPort_[] = "<NewInternalPort xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui2\">";
35. const char _NewInternalPort[] = "</NewInternalPort>";
36.
37. /**< New Internal Client */
38. const char NewInternalClient_[] = "<NewInternalClient xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">";
39. const char _NewInternalClient[] = "</NewInternalClient>";
40.
41. /**< New Enabled */
42. const char NewEnabled[] = "<NewEnabled xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"boolean\">1</NewEnabled>";
43. const char NewEnabled_[] = "<NewEnabled xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"boolean\">";
44. const char _NewEnabled[] = "</NewEnabled>";
45.
46. /**< New Port Mapping Description */
47. const char NewPortMappingDescription_[] = "<NewPortMappingDescription xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"string\">";
48. const char _NewPortMappingDescription[] = "</NewPortMappingDescription>";
49.
50. /**< New Lease Duration */
51. const char NewLeaseDuration[] = "<NewLeaseDuration xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui4\">0</NewLeaseDuration>";
52. const char NewLeaseDuration_[] = "<NewLeaseDuration xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"ui4\">";
53. const char _NewLeaseDuration[] = "</NewLeaseDuration>";
54.
1. /**
2. * @brief This function makes the Add Port Control message in SOAP.
3. * @param dest:Target string pointer
4. * @param protocol:Protocol type
5. * @param extertnal_port:External port
6. * @param internal_ip:Internal IP address
7. * @param internal_port:Internal port
8. * @param description:Description
9. * @return none
10. */
11. void MakeSOAPAddControl(char *dest, const char *protocol, const unsigned int extertnal_port, const char * internal_ip, const unsigned int internal_port, const char *description)
12. {
13. char local_port[6] = {'\0'};
14. strcat(dest, soap_start);
15. strcat(dest, AddPortMapping_);
16. strcat(dest, NewRemoteHost_);
17. strcat(dest, _NewRemoteHost);
18. strcat(dest, NewExternalPort_);
19. sprintf(local_port, "%d", extertnal_port);
20. strcat(dest, local_port);
21. strcat(dest, _NewExternalPort);
22. strcat(dest, NewProtocol_);
23. strcat(dest, protocol);
24. strcat(dest, _NewProtocol);
25. strcat(dest, NewInternalPort_);
26. sprintf(local_port, "%d", internal_port);
27. strcat(dest, local_port);
28. strcat(dest, _NewInternalPort);
29. strcat(dest, NewInternalClient_);
30. strcat(dest, internal_ip);
31. strcat(dest, _NewInternalClient);
32. strcat(dest, NewEnabled);
33. strcat(dest, NewPortMappingDescription_);
34. strcat(dest, description);
35. strcat(dest, _NewPortMappingDescription);
36. strcat(dest, NewLeaseDuration);
37. strcat(dest, _AddPortMapping);
38. strcat(dest, soap_end);
39. }
Then, the HTTP header content is created through the MakePOSTHeader() function. The specific content is as follows:
1. /**
2. * @brief This function makes the HTTP POST Header.
3. * @param dest:Target string pointer
4. * @param content_length: content length
5. * @param action: action type
6. * @return none
7. */
8. void MakePOSTHeader(char *dest, int content_length, int action)
9. {
10. char local_length[6] = {'\0'}, local_port[6] = {'\0'};
11. sprintf(local_length, "%d", content_length);
12. strcat(dest, "POST ");
13. strcat(dest, controlURL);
14. strcat(dest, " HTTP/1.1\r\n");
15. strcat(dest, "Content-Type: text/xml; charset=\"utf-8\"\r\n");
16. strcat(dest, "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#");
17. switch (action)
18. {
19. case DELETE_PORT:
20. strcat(dest, "DeletePortMapping\"");
21. break;
22. case ADD_PORT:
23. strcat(dest, "AddPortMapping\"");
24. break;
25. }
26. strcat(dest, "\r\nUser-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n");
27. strcat(dest, "Host: ");
28. strcat(dest, descIP);
29. sprintf(local_port, ":%s", descPORT);
30. strcat(dest, local_port);
31. strcat(dest, "\r\nContent-Length: ");
32. strcat(dest, local_length);
33. strcat(dest, "\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n");
34. }
35.
Finally, the request is sent, and then the response content is parsed using the parseAddPort() function to determine whether the port mapping was successfully added.
1. /**
2. * @brief This function parses the received add port message from IGD(Internet Gateway Device).
3. * @return 0: success, 1: received xml parse error, other: UPnP error code
4. */
5. signed short parseAddPort(
6. const char *xml /**< string for parse */
7. )
8. {
9. parseHTTP(xml);
10. if (strstr(xml, "u:AddPortMappingResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"") == NULL)
11. {
12. return parseError(xml);
13. }
14.
15. return 0;
16. }
Step 8: Delete an UPnP port mapping table
1. if (choice[0] == '8')
2. {
3. bTreat = SET;
4.
5. printf("\r\nType a Protocol(TCP/UDP) : ");
6. memset(msg, 0, sizeof(msg));
7. scanf("%s", msg);
8. printf("%s\r\n", msg);
9. //GetInputString(msg);
10. strncpy(protocol, msg, 3);
11. protocol[3] = '\0';
12.
13. printf("\r\nType a External Port Number : ");
14.
15. // TCP_LISTEN_PORT=num;
16. // UDP_LISTEN_PORT=num;
17. // printf("%d\r\n",TCP_LISTEN_PORT);
18. memset(msg, 0, sizeof(msg));
19. scanf("%s", msg);
20. printf("%s\r\n", msg);
21. external_port = ATOI(msg, 10);
22.
23. // Try to Delete Port Action
24. if ((ret = DeletePortProcess(sn, protocol, external_port)) == 0)
25. printf("DeletePort Success!!\r\n");
26. else
27. printf("DeletePort Error Code is %d\r\n", ret);
28. }
29.
30. /* OTHERS CHOICE*/
31. if (bTreat == RESET)
32. {
33. printf(" wrong choice \r\n");
34. }
35.
36. eventing_listener(sn3);
37.
38. } /* While(1)*/
39. } /* Main_Menu */
40.
Here, we need to specify the protocol type (TCP or UDP) for the external input deletion port mapping, as well as the external port number. After the input is completed, the AddPortMapping processing is executed through the DeletePortProcess() function. The content of the DeletePortProcess() function is as follows:
1. /**
2. * @brief This function processes the delete port to IGD(Internet Gateway Device).
3. * @return 0: success, -2: Invalid UPnP Step, -1: reply packet timeout, 1: received xml parse error, other: UPnP error code
4. */
5. signed short DeletePortProcess(
6. SOCKET sockfd, /**< a socket number. */
7. const char *protocol, /**< a procotol name. "TCP" or "UDP" */
8. const unsigned int extertnal_port /**< an external port number. */
9. )
10. {
11. short len = 0;
12. long endTime = 0;
13. unsigned long ipaddr;
14. unsigned short port;
15.
16. // Check UPnP Step
17. if (UPnP_Step < 2) return -2;
18.
19. // Make "Delete Port" XML(SOAP)
20. memset(content, '\0', CONT_BUFFER_SIZE);
21. MakeSOAPDeleteControl(content, protocol, extertnal_port);
22.
23. // Make HTTP POST Header
24. memset(send_buffer, '\0', SEND_BUFFER_SIZE);
25. len = strlen(content);
26. MakePOSTHeader(send_buffer, len, DELETE_PORT);
27. strcat(send_buffer, content);
28.
29. //#ifdef UPNP_DEBUG
30. printf("%s\r\n", send_buffer);
31. //#endif
32.
33. ipaddr = inet_addr((unsigned char *)descIP);
34. ipaddr = swapl(ipaddr);
35. port = ATOI(descPORT, 10);
36.
37. // Connect to IGD(Internet Gateway Device)
38. close(sockfd);
39. socket(sockfd, Sn_MR_TCP, PORT_UPNP, Sn_MR_ND); /*Open a port of the socket*/
40. while (getSn_SR(sockfd) != SOCK_INIT);
41.
42. if (connect(sockfd, (unsigned char *)&ipaddr, port) == 0)
43. printf("TCP Socket Error!!\r\n");
44.
45. // Send "Delete Port" Message
46. while (getSn_SR(sockfd) != SOCK_ESTABLISHED);
47. send(sockfd, (void *)send_buffer, strlen(send_buffer));
48.
49. // Receive Reply
50. memset(recv_buffer, '\0', RECV_BUFFER_SIZE);
51. delay_ms(500);
52. endTime = my_time + 3;
53. while (recv(sockfd, (void *)recv_buffer, RECV_BUFFER_SIZE) <= 0 && my_time < endTime); // Check Receive Buffer
54. if (my_time >= endTime)
55. { // Check Timeout
56. close(sockfd);
57. return -1;
58. }
59.
60. // TCP Socket Close
61. close(sockfd);
62.
63. //#ifdef UPNP_DEBUG
64. printf("\r\nReceiveData\r\n%s\r\n", recv_buffer);
65. //#endif
66.
67. // Parse Replied Message
68. return parseDeletePort(recv_buffer);
First, the XML part of the request message will be assembled through the MakeSOAPDeleteControl() function. The details are as follows:
1. /**
2. * @brief This function makes the Delete Port Control message in SOAP.
3. * @param dest:Target string pointer
4. * @param protocol:Protocol type
5. * @param extertnal_port:External port
6. * @return none
7. */
8. void MakeSOAPDeleteControl(char *dest, const char *protocol, const unsigned int extertnal_port)
9. {
10. char local_port[6] = {'\0'};
11. strcat(dest, soap_start);
12. strcat(dest, DeletePortMapping_);
13. strcat(dest, NewRemoteHost_);
14. strcat(dest, _NewRemoteHost);
15. strcat(dest, NewExternalPort_);
16. sprintf(local_port, "%d", extertnal_port);
17. strcat(dest, local_port);
18. strcat(dest, _NewExternalPort);
19. strcat(dest, NewProtocol_);
20. strcat(dest, protocol);
21. strcat(dest, _NewProtocol);
22. strcat(dest, _DeletePortMapping);
23. strcat(dest, soap_end);
24. }
Then, the HTTP header content is created through the MakePOSTHeader() function. The specific content is as follows:
1. /**
2. * @brief This function makes the HTTP POST Header.
3. * @param dest:Target string pointer
4. * @param content_length: content length
5. * @param action: action type
6. * @return none
7. */
8. void MakePOSTHeader(char *dest, int content_length, int action)
9. {
10. char local_length[6] = {'\0'}, local_port[6] = {'\0'};
11. sprintf(local_length, "%d", content_length);
12. strcat(dest, "POST ");
13. strcat(dest, controlURL);
14. strcat(dest, " HTTP/1.1\r\n");
15. strcat(dest, "Content-Type: text/xml; charset=\"utf-8\"\r\n");
16. strcat(dest, "SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#");
17. switch (action)
18. {
19. case DELETE_PORT:
20. strcat(dest, "DeletePortMapping\"");
21. break;
22. case ADD_PORT:
23. strcat(dest, "AddPortMapping\"");
24. break;
25. }
26. strcat(dest, "\r\nUser-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n");
27. strcat(dest, "Host: ");
28. strcat(dest, descIP);
29. sprintf(local_port, ":%s", descPORT);
30. strcat(dest, local_port);
31. strcat(dest, "\r\nContent-Length: ");
32. strcat(dest, local_length);
33. strcat(dest, "\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n\r\n");
34. }
Finally, the request is sent, and then the response content is parsed using the parseDeletePort() function to determine whether the port mapping was successfully added.
1. /**
2. * @brief This function parses the received delete port message from IGD(Internet Gateway Device).
3. * @return 0: success, 1: received xml parse error, other: UPnP error code
4. */
5. signed short parseDeletePort(
6. const char *xml /**< string for parse */
7. )
8. {
9. parseHTTP(xml);
10. if (strstr(xml, "u:DeletePortMappingResponse xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"") == NULL)
11. {
12. return parseError(xml);
13. }
14.
15. return 0;
16. }
7 Run results
After the burning routine was executed, the PHY link was first detected, and then the network address was obtained through DHCP and the network address information was printed:
Next, search for IGD devices. After the search is successful, the device description will be retrieved and the subscription of IGD events will be set. Once all operations are completed successfully, the main menu will be entered.
Next, we input 7 and add a TCP protocol port mapping. The external port is 12345 and the internal port is 8000.
we have added. (UPnP Wizard download link: https://upnp-wizard.en.softonic.com/)
Then we input 5 and start the TCP loopback test program.
Subsequently, we opened a network debugging tool, such as SocketTester, and selected the TCP Client mode. The server address was the external IP address, which was 192.168.1.135, and the port number was the external port number 12345. After clicking "Connect" to establish the connection, we could see that it was successfully connected to the internal W55MH32. The same process was followed for UDP, and this will not be demonstrated here.
Then we input "Q" to exit the loop test program, and then input "8" to delete the 12345 external port of the previously added TCP protocol. Click "Refresh" on the UPnP Wizard, and you can see that it has been successfully deleted. Then execute the loop test program again, and you will not be able to connect to the internal W55MH32.
8 Summary
This article explains how to implement the port forwarding function of the UPnP protocol on the W55MH32 chip. Through practical examples, it details the complete process from device search, obtaining device descriptions, subscribing to events, to adding and deleting port mappings, including the protocol messages involved in each step, function implementations, and specific operations. The article also analyzes the introduction, features, and application scenarios of the UPnP protocol, helping readers understand its practical application value in the interconnection of network devices.
The next article will focus on the TFTP protocol, analyzing its core principles and application in file transfer, and explaining how to implement the TFTP function on the W55MH32. Stay tuned!
WIZnet is a non-fabrication semiconductor company founded in 1998. Its products include the Internet processor iMCU™, which adopts TOE (TCP/IP Offloading Engine) technology and is based on a unique patented fully hardwired TCP/IP. iMCU™ is designed for embedded Internet devices in various applications.
WIZnet has over 70 distributors worldwide, with offices in Hong Kong, South Korea, and the United States, providing technical support and product marketing.
The region managed by the Hong Kong office includes: Australia, India, Turkey, and Asia (excluding South Korea and Japan).