1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_lldp_sample, LOG_LEVEL_DBG);
9
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12
13 #include <zephyr/net/net_core.h>
14 #include <zephyr/net/net_l2.h>
15 #include <zephyr/net/net_if.h>
16 #include <zephyr/net/ethernet.h>
17
18 static struct lldp_system_name_tlv {
19 uint16_t type_length;
20 uint8_t name[4];
21 } __packed tlv = {
22 .name = { 't', 'e', 's', 't' },
23 };
24
set_optional_tlv(struct net_if * iface)25 static void set_optional_tlv(struct net_if *iface)
26 {
27 NET_DBG("");
28
29 tlv.type_length = htons((LLDP_TLV_SYSTEM_NAME << 9) |
30 ((sizeof(tlv) - sizeof(uint16_t)) & 0x01ff));
31
32 net_lldp_config_optional(iface, (uint8_t *)&tlv, sizeof(tlv));
33 }
34
35 /* User data for the interface callback */
36 struct ud {
37 struct net_if *first;
38 struct net_if *second;
39 struct net_if *third;
40 };
41
iface_cb(struct net_if * iface,void * user_data)42 static void iface_cb(struct net_if *iface, void *user_data)
43 {
44 struct ud *ud = user_data;
45
46 if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
47 return;
48 }
49
50 if (!ud->first) {
51 ud->first = iface;
52 return;
53 }
54
55 if (!ud->second) {
56 ud->second = iface;
57 return;
58 }
59
60 if (!ud->third) {
61 ud->third = iface;
62 return;
63 }
64 }
65
setup_iface(struct net_if * iface,const char * ipv6_addr,const char * ipv4_addr,uint16_t vlan_tag)66 static int setup_iface(struct net_if *iface, const char *ipv6_addr,
67 const char *ipv4_addr, uint16_t vlan_tag)
68 {
69 struct net_if_addr *ifaddr;
70 struct in_addr addr4;
71 struct in6_addr addr6;
72 int ret;
73
74 ret = net_eth_vlan_enable(iface, vlan_tag);
75 if (ret < 0) {
76 LOG_ERR("Cannot enable VLAN for tag %d (%d)", vlan_tag, ret);
77 }
78
79 if (net_addr_pton(AF_INET6, ipv6_addr, &addr6)) {
80 LOG_ERR("Invalid address: %s", ipv6_addr);
81 return -EINVAL;
82 }
83
84 ifaddr = net_if_ipv6_addr_add(iface, &addr6, NET_ADDR_MANUAL, 0);
85 if (!ifaddr) {
86 LOG_ERR("Cannot add %s to interface %p", ipv6_addr, iface);
87 return -EINVAL;
88 }
89
90 if (net_addr_pton(AF_INET, ipv4_addr, &addr4)) {
91 LOG_ERR("Invalid address: %s", ipv4_addr);
92 return -EINVAL;
93 }
94
95 ifaddr = net_if_ipv4_addr_add(iface, &addr4, NET_ADDR_MANUAL, 0);
96 if (!ifaddr) {
97 LOG_ERR("Cannot add %s to interface %p", ipv4_addr, iface);
98 return -EINVAL;
99 }
100
101 LOG_DBG("Interface %p VLAN tag %d setup done.", iface, vlan_tag);
102
103 return 0;
104 }
105
106 static struct ud ud;
107
init_vlan(void)108 static int init_vlan(void)
109 {
110 enum ethernet_hw_caps caps;
111 int ret;
112
113 (void)memset(&ud, 0, sizeof(ud));
114
115 net_if_foreach(iface_cb, &ud);
116
117 caps = net_eth_get_hw_capabilities(ud.first);
118 if (!(caps & ETHERNET_HW_VLAN)) {
119 LOG_DBG("Interface %p does not support %s", ud.first, "VLAN");
120 return -ENOENT;
121 }
122
123 /* This sample has two VLANs. For the second one we need to manually
124 * create IP address for this test. But first the VLAN needs to be
125 * added to the interface so that IPv6 DAD can work properly.
126 */
127 ret = setup_iface(ud.second,
128 CONFIG_NET_SAMPLE_IFACE2_MY_IPV6_ADDR,
129 CONFIG_NET_SAMPLE_IFACE2_MY_IPV4_ADDR,
130 CONFIG_NET_SAMPLE_IFACE2_VLAN_TAG);
131 if (ret < 0) {
132 return ret;
133 }
134
135 ret = setup_iface(ud.third,
136 CONFIG_NET_SAMPLE_IFACE3_MY_IPV6_ADDR,
137 CONFIG_NET_SAMPLE_IFACE3_MY_IPV4_ADDR,
138 CONFIG_NET_SAMPLE_IFACE3_VLAN_TAG);
139 if (ret < 0) {
140 return ret;
141 }
142
143 return 0;
144 }
145
parse_lldp(struct net_if * iface,struct net_pkt * pkt)146 static enum net_verdict parse_lldp(struct net_if *iface, struct net_pkt *pkt)
147 {
148 LOG_DBG("iface %p Parsing LLDP, len %zu", iface, net_pkt_get_len(pkt));
149
150 net_pkt_cursor_init(pkt);
151
152 while (1) {
153 uint16_t type_length;
154 uint16_t length;
155 uint8_t type;
156
157 if (net_pkt_read_be16(pkt, &type_length)) {
158 LOG_DBG("End LLDP DU TLV");
159 break;
160 }
161
162 length = type_length & 0x1FF;
163 type = (uint8_t)(type_length >> 9);
164
165 /* Skip for now data */
166 if (net_pkt_skip(pkt, length)) {
167 LOG_DBG("");
168 break;
169 }
170
171 switch (type) {
172 case LLDP_TLV_CHASSIS_ID:
173 LOG_DBG("Chassis ID");
174 break;
175 case LLDP_TLV_PORT_ID:
176 LOG_DBG("Port ID");
177 break;
178 case LLDP_TLV_TTL:
179 LOG_DBG("TTL");
180 break;
181 default:
182 LOG_DBG("TLV Not parsed");
183 break;
184 }
185
186 LOG_DBG("type_length %u type %u length %u",
187 type_length, type, length);
188 }
189
190 /* Let stack to free the packet */
191 return NET_DROP;
192 }
193
init_app(void)194 static int init_app(void)
195 {
196 enum ethernet_hw_caps caps;
197 int ret;
198
199 ret = init_vlan();
200 if (ret < 0) {
201 LOG_WRN("Cannot setup VLAN (%d)", ret);
202 }
203
204 caps = net_eth_get_hw_capabilities(ud.first);
205 if (!(caps & ETHERNET_LLDP)) {
206 LOG_ERR("Interface %p does not support %s", ud.first, "LLDP");
207 LOG_ERR("Cannot continue!");
208 return -ENOENT;
209 }
210
211 set_optional_tlv(ud.first);
212 net_lldp_register_callback(ud.first, parse_lldp);
213
214 return 0;
215 }
216
main(void)217 int main(void)
218 {
219 /* The application will setup VLAN but does nothing meaningful.
220 * The configuration will enable LLDP support so you should see
221 * LLDPDU messages sent to the network interface.
222 */
223 init_app();
224 return 0;
225 }
226