ciptcpipinterface.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. /*******************************************************************************
  2. * Copyright (c) 2009, Rockwell Automation, Inc.
  3. * All rights reserved.
  4. *
  5. ******************************************************************************/
  6. #include "ciptcpipinterface.h"
  7. #include <string.h>
  8. #include "opener_user_conf.h"
  9. #include "cipcommon.h"
  10. #include "cipconnectionobject.h"
  11. #include "cipmessagerouter.h"
  12. #include "ciperror.h"
  13. #include "cipstring.h"
  14. #include "endianconv.h"
  15. #include "cipethernetlink.h"
  16. #include "opener_api.h"
  17. #include "trace.h"
  18. #include "cipassembly.h"
  19. /* Define constants to initialize the config_capability attribute (#2). These
  20. * are needed as defines because we use them for static initialization. */
  21. #define CFG_CAPS_BOOTP_CLIENT 0x01U /**< Device has BOOTP client */
  22. #define CFG_CAPS_DNS_CLIENT 0x02U /**< Device has DNS client */
  23. #define CFG_CAPS_DHCP_CLIENT 0x04U /**< Device has DHCP client */
  24. #define CFG_CAPS_CFG_SETTABLE 0x10U /**< Interface configuration can be set */
  25. #define CFG_CAPS_CFG_CHG_NEEDS_RESET 0x40U /**< Interface configuration change needs RESET */
  26. #define CFG_CAPS_ACD_CAPABLE 0x80U /**< Device supports ACD */
  27. /* OPENER_TCPIP_IFACE_CFG_SETTABLE controls if the interface configuration is fully settable.
  28. * Prepare additional defines needed here:
  29. * - IFACE_CFG_SET_MODE is used to initialize the set mode of the affected attributes (3, 5, 6).
  30. * - CFG_CAPS is the matching initial value for .config_capability
  31. */
  32. #if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
  33. 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
  34. #define IFACE_CFG_SET_MODE kSetable
  35. #define CFG_CAPS (CFG_CAPS_DHCP_CLIENT | CFG_CAPS_CFG_SETTABLE | \
  36. CFG_CAPS_CFG_CHG_NEEDS_RESET)
  37. #else
  38. #define IFACE_CFG_SET_MODE kNotSetOrGetable
  39. #define CFG_CAPS (CFG_CAPS_DHCP_CLIENT)
  40. #endif
  41. /** definition of TCP/IP object instance 1 data */
  42. CipTcpIpObject g_tcpip =
  43. {
  44. .status = 0x01, /* attribute #1 TCP status with 1 we indicate that we got a valid configuration from DHCP, BOOTP or NV data */
  45. .config_capability = CFG_CAPS, /* attribute #2 config_capability */
  46. .config_control = 0x02, /* attribute #3 config_control: 0x02 means that the device shall obtain its interface configuration values via DHCP. */
  47. #if 2 != OPENER_ETHLINK_INSTANCE_CNT
  48. /* For the details where the physical_link_object path should point to, depending on the #
  49. * of Ethernet Link objects refer to Vol. 2, Section 5-4.3.2.4 "Physical Link Object". */
  50. .physical_link_object = { /* attribute #4 physical link object */
  51. 2, /* PathSize in 16 Bit chunks */
  52. CIP_ETHERNETLINK_CLASS_CODE, /* Class Code */
  53. OPENER_ETHLINK_INSTANCE_CNT, /* Instance # */
  54. 0 /* Attribute # (not used as this is the EPATH to the EthernetLink object)*/
  55. },
  56. #else
  57. .physical_link_object = { /* attribute #4 physical link object */
  58. 0, /* PathSize in 16 Bit chunks */
  59. 0, /* Class Code */
  60. 0, /* Instance # */
  61. 0 /* Attribute # */
  62. },
  63. #endif /* #if OPENER_ETHLINK_INSTANCE_CNT != 2 */
  64. .interface_configuration = { /* attribute #5 interface_configuration */
  65. 0, /* IP address */
  66. 0, /* NetworkMask */
  67. 0, /* Gateway */
  68. 0, /* NameServer */
  69. 0, /* NameServer2 */
  70. { /* DomainName */
  71. 0, NULL,
  72. }
  73. },
  74. .hostname = { /* attribute #6 hostname */
  75. 0,
  76. NULL
  77. },
  78. .mcast_ttl_value = 1, /* attribute #8 mcast TTL value */
  79. .mcast_config = { /* attribute #9 multicast configuration */
  80. 0, /* use the default allocation algorithm */
  81. 0, /* reserved */
  82. 1, /* we currently use only one multicast address */
  83. 0 /* the multicast address will be allocated on IP address configuration */
  84. },
  85. .select_acd = false,
  86. .encapsulation_inactivity_timeout = 120 /* attribute #13 encapsulation_inactivity_timeout, use a default value of 120 */
  87. };
  88. /************** Static Functions *********************************/
  89. #if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
  90. 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
  91. /** Check for pb being an alphanumerical character
  92. *
  93. * Is slow but avoids issues with the locale if we're NOT in the 'C' locale.
  94. */
  95. static bool isalnum_c(const EipByte byte) {
  96. return
  97. ('a' <= byte && byte <= 'z') ||
  98. ('A' <= byte && byte <= 'Z') ||
  99. ('0' <= byte && byte <= '9');
  100. }
  101. /** Check passed string to conform to the rules for host name labels
  102. *
  103. * @param label pointer to the label string to check
  104. * @return true if label is valid
  105. *
  106. * A host name label is a string of length 1 to 63 characters with
  107. * the characters of the string conforming to this rules:
  108. * - 1st character: [A-Za-z0-9]
  109. * - next character: [-A-Za-z0-9]
  110. * - last character: [A-Za-z0-9]
  111. * The minimum length of 1 is checked but not the maximum length
  112. * that has already been enforced on data reception.
  113. */
  114. static bool IsValidNameLabel(const EipByte *label) {
  115. if (!isalnum_c(*label) ) {
  116. return false;
  117. }
  118. ++label;
  119. while ('\0' != *label && (isalnum_c(*label) || '-' == *label) ) {
  120. ++label;
  121. }
  122. return ('\0' == *label && '-' != label[-1]);
  123. }
  124. /** Check if domain is a valid domain
  125. *
  126. * @param p_domain pointer to domain string to check
  127. * @return true if domain is valid
  128. *
  129. * We check here for domain names that are part of a valid host name.
  130. * - Do not allow leading or trailing dots.
  131. * - Also a single '.' (the root domain) is not allowed.
  132. * - A complete numeric domain is accepted even if it should not.
  133. * - IDN domain names are not supported. Any IDN domain names must
  134. * be converted to punycode (see https://www.punycoder.com/) by
  135. * the user in advance.
  136. */
  137. static bool IsValidDomain(EipByte *domain) {
  138. bool status = true;
  139. OPENER_TRACE_INFO("Enter '%s'->", domain);
  140. if ('.' == *domain) { /* Forbid leading dot */
  141. return false;
  142. }
  143. EipByte *dot = (EipByte *)strchr( (char *)domain, '.' );
  144. if (dot) {
  145. bool rc;
  146. *dot = '\0';
  147. status &= rc = IsValidNameLabel(domain);
  148. OPENER_TRACE_INFO("Checked %d '%s'\n", rc, domain);
  149. if ('\0' != dot[1]) {
  150. status &= IsValidDomain(dot + 1);
  151. }
  152. else { /* Forbid trailing dot */
  153. status = false;
  154. }
  155. *dot = '.';
  156. }
  157. else {
  158. status = IsValidNameLabel(domain);
  159. OPENER_TRACE_INFO("Check end %d '%s'\n", status, domain);
  160. }
  161. return status;
  162. }
  163. /** Check if an IP address is a valid network mask
  164. *
  165. * @param netmask network mask in network byte order
  166. * @return valid status
  167. *
  168. * Check if it is a valid network mask pattern. The pattern 0xffffffff and
  169. * 0x00000000 are considered as invalid.
  170. */
  171. static bool IsValidNetmask(in_addr_t netmask) {
  172. in_addr_t v = ntohl(netmask);
  173. v = ~v; /* Create the host mask */
  174. ++v; /* This must be a power of 2 then */
  175. bool valid = v && !(v & (v - 1) ); /* Check if it is a power of 2 */
  176. return valid && (INADDR_BROADCAST != netmask);
  177. }
  178. /** Check if an IP address is in one of the network classes A, B or C
  179. *
  180. * @param ip_addr IP address in network byte order
  181. * @return status
  182. *
  183. * Check if the IP address belongs to the network classes A, B or C.
  184. */
  185. static bool IsInClassAbc(in_addr_t ip_addr) {
  186. in_addr_t ip = ntohl(ip_addr);
  187. return IN_CLASSA(ip) || IN_CLASSB(ip) || IN_CLASSC(ip);
  188. }
  189. /** Check if an IP address is on the loopback network
  190. *
  191. * @param ip_addr IP address in network byte order
  192. * @return status
  193. *
  194. * Check if the IP address belongs to the loopback network
  195. * 127.0.0.0 - 127.255.255.255.
  196. */
  197. static bool IsOnLoopbackNetwork(in_addr_t ip_addr) {
  198. in_addr_t ip = ntohl(ip_addr);
  199. return (ip & IN_CLASSA_NET) == (INADDR_LOOPBACK & IN_CLASSA_NET);
  200. }
  201. /** Check if an IP address is either the network or the broadcast address
  202. *
  203. * @param ip_addr IP address in network byte order
  204. * @param net_mask network mask in network byte order
  205. * @return status
  206. *
  207. * Check if an IP address is either the network or the broadcast address.
  208. * In this case it is not a valid IP address for a host.
  209. * This check is endian agnostic.
  210. */
  211. static bool IsNetworkOrBroadcastIp(in_addr_t ip_addr,
  212. in_addr_t net_mask) {
  213. return ( (ip_addr & net_mask) == ip_addr ) || /* is network address */
  214. ( (ip_addr | ~net_mask) == ip_addr ); /* is broadcast address */
  215. }
  216. /** Check the Interface configuration being valid according to EIP specification
  217. *
  218. * In Vol. 2 the "Table 5-4.3 Instance Attributes" provides some information
  219. * which checks should be carried out on the Interface configuration's IP
  220. * addresses. Also there are some hints in the
  221. * Figure 5-4.1 "Diagram Showing the Behavior of the TCP/IP Object".
  222. *
  223. * The following checks may carried out on the IP addresses:
  224. * - N0: IP is not 0 aka. INADDR_ANY
  225. * - MASK: IP is a valid network mask
  226. * - ABC: IP is in class A, B or C
  227. * - NLCL: IP is not localhost aka. INADDR_LOOPBACK
  228. * - NB: IP is neither network or broadcast address (using network_mask)
  229. *
  230. * This is the table which checks are applied to what IP:
  231. * N0 | MASK | ABC | NLCL | NB | IP address
  232. * + | - | + | + | + | ip_address
  233. * - | + | - | - | - | network_mask
  234. * - | - | + | + | + | gateway
  235. * - | - | + | - | - | name_server / name_server_2
  236. * A configured gateway must be reachable according to the network mask.
  237. */
  238. static bool IsValidNetworkConfig(const CipTcpIpInterfaceConfiguration *if_cfg) {
  239. if (INADDR_ANY == ntohl(if_cfg->ip_address) ) { /* N0 */
  240. return false;
  241. }
  242. if (INADDR_ANY != ntohl(if_cfg->network_mask) && /* MASK */
  243. !IsValidNetmask(if_cfg->network_mask) ) {
  244. return false;
  245. }
  246. if (!IsInClassAbc(if_cfg->ip_address) || /* ABC */
  247. !IsInClassAbc(if_cfg->gateway) ||
  248. !IsInClassAbc(if_cfg->name_server) ||
  249. !IsInClassAbc(if_cfg->name_server_2) ) {
  250. return false;
  251. }
  252. if (IsOnLoopbackNetwork(if_cfg->ip_address) || /* NLCL */
  253. IsOnLoopbackNetwork(if_cfg->gateway) ) {
  254. return false;
  255. }
  256. /* Check NB */
  257. if (IsNetworkOrBroadcastIp(if_cfg->ip_address, if_cfg->network_mask) ||
  258. (INADDR_ANY != ntohl(if_cfg->gateway) &&
  259. IsNetworkOrBroadcastIp(if_cfg->gateway, if_cfg->network_mask) ) ) {
  260. return false;
  261. }
  262. if (INADDR_ANY != ntohl(if_cfg->gateway) &&
  263. INADDR_ANY != ntohl(if_cfg->network_mask) ) {
  264. /* gateway is configured. Check if it is reachable. */
  265. if ( (if_cfg->network_mask & if_cfg->ip_address) !=
  266. (if_cfg->network_mask & if_cfg->gateway) ) {
  267. return false;
  268. }
  269. }
  270. return true;
  271. }
  272. static bool IsIOConnectionActive(void) {
  273. DoublyLinkedListNode *node = connection_list.first;
  274. while (NULL != node) {
  275. CipConnectionObject *connection = node->data;
  276. if (ConnectionObjectIsTypeIOConnection(connection) &&
  277. kConnectionObjectStateTimedOut !=
  278. ConnectionObjectGetState(connection) ) {
  279. /* An IO connection is present but is only considered active
  280. * if it is NOT in timeout state. */
  281. return true;
  282. }
  283. node = node->next;
  284. }
  285. return false;
  286. }
  287. #endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
  288. static CipUsint dummy_data_field = 0; /**< dummy data fiel to provide non-null data pointers for attributes without data fields */
  289. /************** Functions ****************************************/
  290. void EncodeCipTcpIpInterfaceConfiguration(const void *const data,
  291. ENIPMessage *const outgoing_message)
  292. {
  293. CipTcpIpInterfaceConfiguration *
  294. tcp_ip_network_interface_configuration =
  295. (CipTcpIpInterfaceConfiguration *) data;
  296. AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->ip_address),
  297. outgoing_message);
  298. AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->network_mask),
  299. outgoing_message);
  300. AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->gateway),
  301. outgoing_message);
  302. AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->name_server),
  303. outgoing_message);
  304. AddDintToMessage(ntohl(tcp_ip_network_interface_configuration->name_server_2),
  305. outgoing_message);
  306. EncodeCipString(&(tcp_ip_network_interface_configuration->domain_name),
  307. outgoing_message);
  308. }
  309. void EncodeCipTcpIpMulticastConfiguration(const void *const data,
  310. ENIPMessage *const outgoing_message) {
  311. EncodeCipUsint(&(g_tcpip.mcast_config.alloc_control), outgoing_message);
  312. EncodeCipUsint(&(g_tcpip.mcast_config.reserved_shall_be_zero),
  313. outgoing_message);
  314. EncodeCipUint(&(g_tcpip.mcast_config.number_of_allocated_multicast_addresses),
  315. outgoing_message);
  316. CipUdint multicast_address = ntohl(
  317. g_tcpip.mcast_config.starting_multicast_address);
  318. EncodeCipUdint(&multicast_address, outgoing_message);
  319. }
  320. void EncodeSafetyNetworkNumber(const void *const data,
  321. ENIPMessage *const outgoing_message) {
  322. FillNextNMessageOctetsWithValueAndMoveToNextPosition(0, 6, outgoing_message);
  323. }
  324. void EncodeCipLastConflictDetected(const void *const data,
  325. ENIPMessage *const outgoing_message) {
  326. const size_t kAttribute11Size = sizeof(CipUsint) + 6 * sizeof(CipUsint) + 28 *
  327. sizeof(CipUsint);
  328. OPENER_ASSERT(kAttribute11Size == 35);
  329. FillNextNMessageOctetsWithValueAndMoveToNextPosition(0,
  330. kAttribute11Size,
  331. outgoing_message);
  332. }
  333. int DecodeTcpIpInterfaceConfigurationControl( /* Attribute 3 */
  334. CipDword *const data,
  335. const CipMessageRouterRequest *const message_router_request,
  336. CipMessageRouterResponse *const message_router_response) {
  337. int number_of_decoded_bytes = -1;
  338. CipDword configuration_control_received = GetDintFromMessage(
  339. &(message_router_request->data));
  340. if ((configuration_control_received & kTcpipCfgCtrlMethodMask) >= 0x03
  341. || (configuration_control_received & ~kTcpipCfgCtrlMethodMask)) {
  342. message_router_response->general_status =
  343. kCipErrorInvalidAttributeValue;
  344. } else {
  345. /* Set reserved bits to zero on reception. */
  346. configuration_control_received &= (kTcpipCfgCtrlMethodMask
  347. | kTcpipCfgCtrlDnsEnable);
  348. *data = configuration_control_received;
  349. number_of_decoded_bytes = 4;
  350. message_router_response->general_status = kCipErrorSuccess;
  351. }
  352. return number_of_decoded_bytes;
  353. }
  354. #if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
  355. 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
  356. int DecodeCipTcpIpInterfaceConfiguration( /* Attribute 5 */
  357. CipTcpIpInterfaceConfiguration *const data, //kCipUdintUdintUdintUdintUdintString
  358. const CipMessageRouterRequest *const message_router_request,
  359. CipMessageRouterResponse *const message_router_response) {
  360. int number_of_decoded_bytes = -1;
  361. CipTcpIpInterfaceConfiguration if_cfg;
  362. CipUdint tmp_ip;
  363. if (IsIOConnectionActive()) {
  364. message_router_response->general_status = kCipErrorDeviceStateConflict;
  365. return number_of_decoded_bytes;
  366. }
  367. if (kTcpipCfgCtrlStaticIp
  368. != (g_tcpip.config_control & kTcpipCfgCtrlMethodMask)) {
  369. message_router_response->general_status = kCipErrorObjectStateConflict;
  370. return number_of_decoded_bytes;
  371. }
  372. memset(&if_cfg, 0, sizeof if_cfg);
  373. tmp_ip = GetUdintFromMessage(&(message_router_request->data));
  374. if_cfg.ip_address = htonl(tmp_ip);
  375. tmp_ip = GetUdintFromMessage(&(message_router_request->data));
  376. if_cfg.network_mask = htonl(tmp_ip);
  377. tmp_ip = GetUdintFromMessage(&(message_router_request->data));
  378. if_cfg.gateway = htonl(tmp_ip);
  379. tmp_ip = GetUdintFromMessage(&(message_router_request->data));
  380. if_cfg.name_server = htonl(tmp_ip);
  381. tmp_ip = GetUdintFromMessage(&(message_router_request->data));
  382. if_cfg.name_server_2 = htonl(tmp_ip);
  383. CipUint domain_name_length = GetUintFromMessage(
  384. &(message_router_request->data));
  385. if (domain_name_length > 48) { /* see Vol. 2, Table 5-4.3 Instance Attributes */
  386. message_router_response->general_status = kCipErrorTooMuchData;
  387. return number_of_decoded_bytes;
  388. }
  389. SetCipStringByData(&if_cfg.domain_name, domain_name_length,
  390. message_router_request->data);
  391. domain_name_length = (domain_name_length + 1) & (~0x0001u); /* Align for possible pad byte */
  392. OPENER_TRACE_INFO("Domain: ds %hu '%s'\n",
  393. domain_name_length,
  394. if_cfg.domain_name.string);
  395. if (!IsValidNetworkConfig(&if_cfg)
  396. || (domain_name_length > 0
  397. && !IsValidDomain(if_cfg.domain_name.string))) {
  398. message_router_response->general_status =
  399. kCipErrorInvalidAttributeValue;
  400. return number_of_decoded_bytes;
  401. }
  402. *data = if_cfg; //write data to attribute
  403. number_of_decoded_bytes = 20 + domain_name_length;
  404. /* Tell that this configuration change becomes active after a reset */
  405. g_tcpip.status |= kTcpipStatusIfaceCfgPend;
  406. message_router_response->general_status = kCipErrorSuccess;
  407. return number_of_decoded_bytes;
  408. }
  409. int DecodeCipTcpIpInterfaceHostName( /* Attribute 6 */
  410. CipString *const data,
  411. const CipMessageRouterRequest *const message_router_request,
  412. CipMessageRouterResponse *const message_router_response) {
  413. int number_of_decoded_bytes = -1;
  414. CipString tmp_host_name = {
  415. .length = 0u,
  416. .string = NULL
  417. };
  418. CipUint host_name_length =
  419. GetUintFromMessage(&(message_router_request->data) );
  420. if (host_name_length > 64) { /* see RFC 1123 on more details */
  421. message_router_response->general_status = kCipErrorTooMuchData;
  422. return number_of_decoded_bytes;
  423. }
  424. SetCipStringByData(&tmp_host_name,
  425. host_name_length,
  426. message_router_request->data);
  427. host_name_length = (host_name_length + 1) & (~0x0001u); /* Align for possible pad byte */
  428. OPENER_TRACE_INFO("Host Name: ds %hu '%s'\n",
  429. host_name_length,
  430. tmp_host_name.string);
  431. if (!IsValidNameLabel(tmp_host_name.string) ) {
  432. message_router_response->general_status =
  433. kCipErrorInvalidAttributeValue;
  434. return number_of_decoded_bytes;
  435. }
  436. *data = tmp_host_name; //write data to attribute
  437. /* Tell that this configuration change becomes active after a reset */
  438. g_tcpip.status |= kTcpipStatusIfaceCfgPend;
  439. message_router_response->general_status = kCipErrorSuccess;
  440. return number_of_decoded_bytes;
  441. }
  442. #endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
  443. int DecodeCipTcpIpInterfaceEncapsulationInactivityTimeout( /* Attribute 13 */
  444. CipUint *const data,
  445. const CipMessageRouterRequest *const message_router_request,
  446. CipMessageRouterResponse *const message_router_response) {
  447. int number_of_decoded_bytes = -1;
  448. CipUint inactivity_timeout_received = GetUintFromMessage(
  449. &(message_router_request->data));
  450. if (inactivity_timeout_received > 3600) {
  451. message_router_response->general_status =
  452. kCipErrorInvalidAttributeValue;
  453. } else {
  454. *data = inactivity_timeout_received;
  455. message_router_response->general_status = kCipErrorSuccess;
  456. number_of_decoded_bytes = 2;
  457. }
  458. return number_of_decoded_bytes;
  459. }
  460. EipStatus CipTcpIpInterfaceInit() {
  461. CipClass *tcp_ip_class = NULL;
  462. if ( ( tcp_ip_class = CreateCipClass(kCipTcpIpInterfaceClassCode, /* class code */
  463. 0, /* # class attributes */
  464. 7, /* # highest class attribute number */
  465. 2, /* # class services */
  466. 13, /* # instance attributes */
  467. 13, /* # highest instance attribute number */
  468. 3, /* # instance services */
  469. 1, /* # instances */
  470. "TCP/IP interface", 4, /* # class revision */
  471. NULL /* # function pointer for initialization */
  472. ) ) == 0 ) {
  473. return kEipStatusError;
  474. }
  475. CipInstance *instance = GetCipInstance(tcp_ip_class, 1); /* bind attributes to the instance #1 that was created above */
  476. InsertAttribute(instance,
  477. 1,
  478. kCipDword,
  479. EncodeCipDword,
  480. NULL,
  481. &g_tcpip.status,
  482. kGetableSingleAndAll);
  483. InsertAttribute(instance,
  484. 2,
  485. kCipDword,
  486. EncodeCipDword,
  487. NULL,
  488. &g_tcpip.config_capability,
  489. kGetableSingleAndAll);
  490. InsertAttribute(instance,
  491. 3,
  492. kCipDword,
  493. EncodeCipDword,
  494. DecodeTcpIpInterfaceConfigurationControl,
  495. &g_tcpip.config_control,
  496. kSetAndGetAble | kNvDataFunc | IFACE_CFG_SET_MODE );
  497. InsertAttribute(instance,
  498. 4,
  499. kCipEpath,
  500. EncodeCipEPath,
  501. NULL,
  502. &g_tcpip.physical_link_object,
  503. kGetableSingleAndAll);
  504. #if defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && \
  505. 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE
  506. InsertAttribute(instance,
  507. 5,
  508. kCipUdintUdintUdintUdintUdintString,
  509. EncodeCipTcpIpInterfaceConfiguration,
  510. DecodeCipTcpIpInterfaceConfiguration,
  511. &g_tcpip.interface_configuration,
  512. kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
  513. InsertAttribute(instance,
  514. 6,
  515. kCipString,
  516. EncodeCipString,
  517. DecodeCipTcpIpInterfaceHostName,
  518. &g_tcpip.hostname,
  519. kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
  520. #else
  521. InsertAttribute(instance,
  522. 5,
  523. kCipUdintUdintUdintUdintUdintString,
  524. EncodeCipTcpIpInterfaceConfiguration,
  525. NULL, //not settable
  526. &g_tcpip.interface_configuration,
  527. kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
  528. InsertAttribute(instance,
  529. 6,
  530. kCipString,
  531. EncodeCipString,
  532. NULL, //not settable
  533. &g_tcpip.hostname,
  534. kGetableSingleAndAll | kNvDataFunc | IFACE_CFG_SET_MODE);
  535. #endif /* defined (OPENER_TCPIP_IFACE_CFG_SETTABLE) && 0 != OPENER_TCPIP_IFACE_CFG_SETTABLE*/
  536. InsertAttribute(instance,
  537. 7,
  538. kCipAny,
  539. EncodeSafetyNetworkNumber,
  540. NULL,
  541. &dummy_data_field,
  542. kGetableAllDummy);
  543. InsertAttribute(instance,
  544. 8,
  545. kCipUsint,
  546. EncodeCipUsint,
  547. NULL,
  548. &g_tcpip.mcast_ttl_value,
  549. kGetableSingleAndAll);
  550. InsertAttribute(instance,
  551. 9,
  552. kCipAny,
  553. EncodeCipTcpIpMulticastConfiguration,
  554. NULL,
  555. &g_tcpip.mcast_config,
  556. kGetableSingleAndAll);
  557. InsertAttribute(instance,
  558. 10,
  559. kCipBool,
  560. EncodeCipBool,
  561. NULL,
  562. &g_tcpip.select_acd,
  563. kGetableAllDummy);
  564. InsertAttribute(instance,
  565. 11,
  566. kCipBool,
  567. EncodeCipLastConflictDetected,
  568. NULL,
  569. &dummy_data_field,
  570. kGetableAllDummy);
  571. InsertAttribute(instance,
  572. 12,
  573. kCipBool,
  574. EncodeCipBool,
  575. NULL,
  576. &dummy_data_field, kGetableAllDummy);
  577. InsertAttribute(instance,
  578. 13,
  579. kCipUint,
  580. EncodeCipUint,
  581. DecodeCipTcpIpInterfaceEncapsulationInactivityTimeout,
  582. &g_tcpip.encapsulation_inactivity_timeout,
  583. kSetAndGetAble | kNvDataFunc);
  584. InsertService(tcp_ip_class, kGetAttributeSingle,
  585. &GetAttributeSingle,
  586. "GetAttributeSingle");
  587. InsertService(tcp_ip_class, kGetAttributeAll, &GetAttributeAll,
  588. "GetAttributeAll");
  589. InsertService(tcp_ip_class, kSetAttributeSingle,
  590. &SetAttributeSingle,
  591. "SetAttributeSingle");
  592. return kEipStatusOk;
  593. }
  594. void ShutdownTcpIpInterface(void) {
  595. /*Only free the resources if they are initialized */
  596. if (NULL != g_tcpip.hostname.string) {
  597. CipFree(g_tcpip.hostname.string);
  598. g_tcpip.hostname.string = NULL;
  599. }
  600. if (NULL != g_tcpip.interface_configuration.domain_name.string) {
  601. CipFree(g_tcpip.interface_configuration.domain_name.string);
  602. g_tcpip.interface_configuration.domain_name.string = NULL;
  603. }
  604. }
  605. /**
  606. * This function calculates the multicast base address to be used for CIP
  607. * connections from the current IP setting. The algorithm is implemented
  608. * according to CIP spec Volume 2,
  609. * section 3-5.3 "Multicast Address Allocation for EtherNet/IP"
  610. */
  611. void CipTcpIpCalculateMulticastIp(CipTcpIpObject *const tcpip) {
  612. /* Multicast base address according to spec: 239.192.1.0 */
  613. static const CipUdint cip_mcast_base_addr = 0xEFC00100;
  614. /* Calculate the CIP multicast address. The multicast address is calculated, not input */
  615. CipUdint host_id = ntohl(tcpip->interface_configuration.ip_address) &
  616. ~ntohl(tcpip->interface_configuration.network_mask);
  617. host_id -= 1;
  618. host_id &= 0x3ff;
  619. tcpip->mcast_config.starting_multicast_address =
  620. htonl(cip_mcast_base_addr + (host_id << 5) );
  621. }
  622. EipUint16 GetEncapsulationInactivityTimeout(CipInstance *instance) {
  623. CipAttributeStruct *attribute = GetCipAttribute(instance, 13);
  624. OPENER_ASSERT(NULL != attribute);
  625. CipUint *data = (CipUint *) attribute->data;
  626. EipUint16 encapsulation_inactivity_timeout = *data;
  627. return encapsulation_inactivity_timeout;
  628. }