1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <arpa/inet.h>
5 #include <string>
6 
7 #include "host-ipmid/ipmid-api.h"
8 #include "ipmid.hpp"
9 #include "transporthandler.h"
10 
11 #define SYSTEMD_NETWORKD_DBUS 1
12 
13 #ifdef SYSTEMD_NETWORKD_DBUS
14 #include <systemd/sd-bus.h>
15 #include <mapper.h>
16 #endif
17 
18 // OpenBMC System Manager dbus framework
19 const char  *obj   =  "/org/openbmc/NetworkManager/Interface";
20 const char  *ifc   =  "org.openbmc.NetworkManager";
21 
22 const char *nwinterface = "eth0";
23 
24 const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx
25 
26 struct channel_config_t channel_config;
27 
28 const uint8_t SET_COMPLETE = 0;
29 const uint8_t SET_IN_PROGRESS = 1;
30 const uint8_t SET_COMMIT_WRITE = 2; //Optional
31 const uint8_t SET_IN_PROGRESS_RESERVED = 3; //Reserved
32 
33 // Status of Set-In-Progress Parameter (# 0)
34 uint8_t lan_set_in_progress = SET_COMPLETE;
35 
36 
37 
38 void register_netfn_transport_functions() __attribute__((constructor));
39 
40 // Helper Function to get IP Address/NetMask/Gateway from Network Manager or
41 // Cache based on Set-In-Progress State
42 ipmi_ret_t getNetworkData(uint8_t lan_param, uint8_t * data)
43 {
44     sd_bus *bus = ipmid_get_sd_bus_connection();
45     sd_bus_message *reply = NULL;
46     sd_bus_error error = SD_BUS_ERROR_NULL;
47     int family;
48     unsigned char prefixlen;
49     char* ipaddr = NULL;
50     unsigned long mask = 0xFFFFFFFF;
51     char* gateway = NULL;
52     int r = 0;
53     ipmi_ret_t rc = IPMI_CC_OK;
54     char *app = NULL;
55 
56     r = mapper_get_service(bus, obj, &app);
57     if (r < 0) {
58         fprintf(stderr, "Failed to get %s bus name: %s\n",
59                 obj, strerror(-r));
60         rc = IPMI_CC_UNSPECIFIED_ERROR;
61         goto cleanup;
62     }
63     r = sd_bus_call_method(bus, app, obj, ifc, "GetAddress4", &error,
64                             &reply, "s", nwinterface);
65     if(r < 0)
66     {
67         fprintf(stderr, "Failed to call Get Method: %s\n", strerror(-r));
68         rc = IPMI_CC_UNSPECIFIED_ERROR;
69         goto cleanup;
70     }
71 
72     r = sd_bus_message_read(reply, "iyss",
73                             &family, &prefixlen, &ipaddr, &gateway);
74     if(r < 0)
75     {
76         fprintf(stderr, "Failed to get a response: %s\n", strerror(-rc));
77         rc = IPMI_CC_RESPONSE_ERROR;
78         goto cleanup;
79     }
80 
81     printf("N/W data from HW %s:%d:%s:%s\n",
82             family==AF_INET?"IPv4":"IPv6", prefixlen, ipaddr,gateway);
83     printf("N/W data from Cache: %s:%s:%s\n",
84             channel_config.new_ipaddr.c_str(),
85             channel_config.new_netmask.c_str(),
86             channel_config.new_gateway.c_str());
87 
88     if(lan_param == LAN_PARM_IP)
89     {
90         if(lan_set_in_progress == SET_COMPLETE)
91         {
92             std::string ipaddrstr(ipaddr);
93             inet_pton(AF_INET, ipaddrstr.c_str(),(void *)data);
94         }
95         else if(lan_set_in_progress == SET_IN_PROGRESS)
96         {
97             inet_pton(AF_INET, channel_config.new_ipaddr.c_str(), (void *)data);
98         }
99     }
100     else if(lan_param == LAN_PARM_SUBNET)
101     {
102         if(lan_set_in_progress == SET_COMPLETE)
103          {
104             mask = htonl(mask<<(32-prefixlen));
105             memcpy(data, &mask, 4);
106          }
107          else if(lan_set_in_progress == SET_IN_PROGRESS)
108          {
109              inet_pton(AF_INET, channel_config.new_netmask.c_str(), (void *)data);
110          }
111     }
112     else if(lan_param == LAN_PARM_GATEWAY)
113     {
114         if(lan_set_in_progress == SET_COMPLETE)
115          {
116             std::string gatewaystr(gateway);
117             inet_pton(AF_INET, gatewaystr.c_str(), (void *)data);
118          }
119          else if(lan_set_in_progress == SET_IN_PROGRESS)
120          {
121              inet_pton(AF_INET, channel_config.new_gateway.c_str(),(void *)data);
122          }
123     }
124     else
125     {
126         rc = IPMI_CC_PARM_OUT_OF_RANGE;
127     }
128 
129 cleanup:
130     sd_bus_error_free(&error);
131     reply = sd_bus_message_unref(reply);
132     free(app);
133 
134     return rc;
135 }
136 
137 ipmi_ret_t ipmi_transport_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
138                               ipmi_request_t request, ipmi_response_t response,
139                               ipmi_data_len_t data_len, ipmi_context_t context)
140 {
141     printf("Handling TRANSPORT WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd);
142     // Status code.
143     ipmi_ret_t rc = IPMI_CC_INVALID;
144     *data_len = 0;
145     return rc;
146 }
147 
148 struct set_lan_t {
149     uint8_t channel;
150     uint8_t parameter;
151     uint8_t data[8]; // Per IPMI spec, not expecting more than this size
152 }  __attribute__ ((packed));
153 
154 ipmi_ret_t ipmi_transport_set_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
155                               ipmi_request_t request, ipmi_response_t response,
156                               ipmi_data_len_t data_len, ipmi_context_t context)
157 {
158     ipmi_ret_t rc = IPMI_CC_OK;
159     *data_len = 0;
160     sd_bus *bus = ipmid_get_sd_bus_connection();
161     sd_bus_message *reply = nullptr;
162     sd_bus_error error = SD_BUS_ERROR_NULL;
163     int r = 0;
164     char *app = nullptr;
165 
166     char tmp_ipaddr[INET_ADDRSTRLEN];
167     char tmp_netmask[INET_ADDRSTRLEN];
168     char tmp_gateway[INET_ADDRSTRLEN];
169 
170     printf("IPMI SET_LAN\n");
171 
172     set_lan_t *reqptr = (set_lan_t*) request;
173 
174     if (reqptr->parameter == LAN_PARM_IP) {
175         snprintf(tmp_ipaddr, INET_ADDRSTRLEN, "%d.%d.%d.%d",
176             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
177         channel_config.new_ipaddr.assign(tmp_ipaddr);
178     } else if (reqptr->parameter == LAN_PARM_MAC) {
179         char mac[SIZE_MAC];
180 
181         snprintf(mac, SIZE_MAC, "%02x:%02x:%02x:%02x:%02x:%02x",
182                 reqptr->data[0],
183                 reqptr->data[1],
184                 reqptr->data[2],
185                 reqptr->data[3],
186                 reqptr->data[4],
187                 reqptr->data[5]);
188 
189         r = mapper_get_service(bus, obj, &app);
190         if (r < 0) {
191             fprintf(stderr, "Failed to get %s bus name: %s\n",
192                     obj, strerror(-r));
193             goto finish;
194         }
195         r = sd_bus_call_method(bus, app, obj, ifc, "SetHwAddress", &error,
196                                 &reply, "ss", nwinterface, mac);
197         if (r < 0) {
198             fprintf(stderr, "Failed to call the method: %s\n", strerror(-r));
199             rc = IPMI_CC_UNSPECIFIED_ERROR;
200         }
201     } else if (reqptr->parameter == LAN_PARM_SUBNET)
202     {
203         snprintf(tmp_netmask, INET_ADDRSTRLEN, "%d.%d.%d.%d",
204             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
205         channel_config.new_netmask.assign(tmp_netmask);
206     } else if (reqptr->parameter == LAN_PARM_GATEWAY)
207     {
208         snprintf(tmp_gateway, INET_ADDRSTRLEN, "%d.%d.%d.%d",
209             reqptr->data[0], reqptr->data[1], reqptr->data[2], reqptr->data[3]);
210         channel_config.new_gateway.assign(tmp_gateway);
211     } else if (reqptr->parameter == LAN_PARM_INPROGRESS)
212     {
213         if(reqptr->data[0] == SET_COMPLETE) {
214             lan_set_in_progress = SET_COMPLETE;
215 
216             printf("N/W data from Cache: %s:%s:%s\n",
217                     channel_config.new_ipaddr.c_str(),
218                     channel_config.new_netmask.c_str(),
219                     channel_config.new_gateway.c_str());
220             printf("Use Set Channel Access command to apply them\n");
221 
222         } else if(reqptr->data[0] == SET_IN_PROGRESS) // Set In Progress
223         {
224             lan_set_in_progress = SET_IN_PROGRESS;
225         }
226     } else
227     {
228         fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
229         rc = IPMI_CC_PARM_NOT_SUPPORTED;
230     }
231 
232 finish:
233     sd_bus_error_free(&error);
234     reply = sd_bus_message_unref(reply);
235     free(app);
236 
237     return rc;
238 }
239 
240 struct get_lan_t {
241     uint8_t rev_channel;
242     uint8_t parameter;
243     uint8_t parameter_set;
244     uint8_t parameter_block;
245 }  __attribute__ ((packed));
246 
247 ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
248                               ipmi_request_t request, ipmi_response_t response,
249                               ipmi_data_len_t data_len, ipmi_context_t context)
250 {
251     ipmi_ret_t rc = IPMI_CC_OK;
252     *data_len = 0;
253     sd_bus *bus = ipmid_get_sd_bus_connection();
254     sd_bus_message *reply = NULL;
255     sd_bus_error error = SD_BUS_ERROR_NULL;
256     int r = 0;
257     const uint8_t current_revision = 0x11; // Current rev per IPMI Spec 2.0
258     int i = 0;
259     char *app = NULL;
260 
261     printf("IPMI GET_LAN\n");
262 
263     get_lan_t *reqptr = (get_lan_t*) request;
264 
265     if (reqptr->rev_channel & 0x80) // Revision is bit 7
266     {
267         // Only current revision was requested
268         *data_len = sizeof(current_revision);
269         memcpy(response, &current_revision, *data_len);
270         return IPMI_CC_OK;
271     }
272 
273     // TODO Use dbus interface once available. For now use ip cmd.
274     // TODO Add the rest of the parameters, like gateway
275 
276     if (reqptr->parameter == LAN_PARM_INPROGRESS)
277     {
278         uint8_t buf[] = {current_revision, lan_set_in_progress};
279         *data_len = sizeof(buf);
280         memcpy(response, &buf, *data_len);
281     }
282     else if (reqptr->parameter == LAN_PARM_AUTHSUPPORT)
283     {
284         uint8_t buf[] = {current_revision,0x04};
285         *data_len = sizeof(buf);
286         memcpy(response, &buf, *data_len);
287     }
288     else if (reqptr->parameter == LAN_PARM_AUTHENABLES)
289     {
290         uint8_t buf[] = {current_revision,0x04,0x04,0x04,0x04,0x04};
291         *data_len = sizeof(buf);
292         memcpy(response, &buf, *data_len);
293     }
294     else if ((reqptr->parameter == LAN_PARM_IP) || (reqptr->parameter == LAN_PARM_SUBNET) || (reqptr->parameter == LAN_PARM_GATEWAY))
295     {
296         uint8_t buf[5];
297 
298         *data_len = sizeof(current_revision);
299         memcpy(buf, &current_revision, *data_len);
300 
301         if(getNetworkData(reqptr->parameter, &buf[1]) == IPMI_CC_OK)
302         {
303             *data_len = sizeof(buf);
304             memcpy(response, &buf, *data_len);
305         }
306         else
307         {
308             rc = IPMI_CC_UNSPECIFIED_ERROR;
309         }
310     }
311     else if (reqptr->parameter == LAN_PARM_MAC)
312     {
313         //string to parse: link/ether xx:xx:xx:xx:xx:xx
314         uint8_t buf[7];
315         char *eaddr1 = NULL;
316 
317         r = mapper_get_service(bus, obj, &app);
318         if (r < 0) {
319             fprintf(stderr, "Failed to get %s bus name: %s\n",
320                     obj, strerror(-r));
321             goto cleanup;
322         }
323         r = sd_bus_call_method(bus, app, obj, ifc, "GetHwAddress", &error,
324                                 &reply, "s", nwinterface);
325         if(r < 0)
326         {
327             fprintf(stderr, "Failed to call Get Method: %s\n", strerror(-r));
328             rc = IPMI_CC_UNSPECIFIED_ERROR;
329             goto cleanup;
330         }
331 
332         r = sd_bus_message_read(reply, "s", &eaddr1);
333         if (r < 0)
334         {
335             fprintf(stderr, "Failed to get a response: %s", strerror(-r));
336             rc = IPMI_CC_UNSPECIFIED_ERROR;
337             goto cleanup;
338         }
339         if (eaddr1 == NULL)
340         {
341             fprintf(stderr, "Failed to get a valid response: %s", strerror(-r));
342             rc = IPMI_CC_UNSPECIFIED_ERROR;
343             goto cleanup;
344         }
345 
346         memcpy((void*)&buf[0], &current_revision, 1);
347 
348         char *tokptr = NULL;
349         char* digit = strtok_r(eaddr1, ":", &tokptr);
350         if (digit == NULL)
351         {
352             fprintf(stderr, "Unexpected MAC format: %s", eaddr1);
353             rc = IPMI_CC_RESPONSE_ERROR;
354             goto cleanup;
355         }
356 
357         i=0;
358         while (digit != NULL)
359         {
360             int resp_byte = strtoul(digit, NULL, 16);
361             memcpy((void*)&buf[i+1], &resp_byte, 1);
362             i++;
363             digit = strtok_r(NULL, ":", &tokptr);
364         }
365 
366         *data_len = sizeof(buf);
367         memcpy(response, &buf, *data_len);
368     }
369     else
370     {
371         fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
372         rc = IPMI_CC_PARM_NOT_SUPPORTED;
373     }
374 
375 cleanup:
376     sd_bus_error_free(&error);
377     reply = sd_bus_message_unref(reply);
378     free(app);
379 
380     return rc;
381 }
382 
383 void register_netfn_transport_functions()
384 {
385     // <Wildcard Command>
386     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_WILDCARD);
387     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_WILDCARD, NULL, ipmi_transport_wildcard,
388                            PRIVILEGE_USER);
389 
390     // <Set LAN Configuration Parameters>
391     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_SET_LAN);
392     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_SET_LAN, NULL, ipmi_transport_set_lan,
393                            PRIVILEGE_ADMIN);
394 
395     // <Get LAN Configuration Parameters>
396     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_TRANSPORT, IPMI_CMD_GET_LAN);
397     ipmi_register_callback(NETFUN_TRANSPORT, IPMI_CMD_GET_LAN, NULL, ipmi_transport_get_lan,
398                            PRIVILEGE_OPERATOR);
399 
400     return;
401 }
402