librmnetctl: Egress qos for rmnet driver
Additional flow control support added for rmnet driver.
Supported by the addition of IFLA_VLAN_EGRESS_QOS netlink
attributes in rmnet driver and this RTM_NEWLINK message
configuration api.
• flow_activate (uint16 bearer_id, uint32 flow_id, uint8 ip_type, uint32 tcm_handle)
• flow_delete (uint16 bearer_id)
• flow_control (uint16 bearer_id, uint32 grant_size, uint16 seq, uint8 ack)
• flow_state_up (uint16 instance, uint32 ep_type, uint32 iface_id)
• flow_state_down (uint16 instance)
CRs-Fixed: 2224576
Change-Id: Ib8b337c840631ef8a3d36884e42a4faea153ba18
diff --git a/rmnetctl/cli/rmnetcli.c b/rmnetctl/cli/rmnetcli.c
index c02850b..a40be0a 100644
--- a/rmnetctl/cli/rmnetcli.c
+++ b/rmnetctl/cli/rmnetcli.c
@@ -222,6 +222,31 @@
printf(_2TABS" by inputting dev name\n\n");
printf("rmnetcli -n bridgelink <dev_name> Bridge a vnd and a dev");
printf(_2TABS" <vnd id> by specifying dev id and vnd id\n\n");
+ printf("rmnetcli -n flowactivate <real dev> activate a flow\n");
+ printf(_2TABS" <vnd_name> string - vnd device name\n\n");
+ printf(_2TABS" <bearer_id> int - bearer id\n\n");
+ printf(_2TABS" <flow id> int - flow id\n\n");
+ printf(_2TABS" <ip type> int - ip type\n\n");
+ printf(_2TABS" <handle> int - flow handle\n\n");
+ printf("rmnetcli -n flowdel <real dev> delete a flow\n");
+ printf(_2TABS" <vnd_name> string - vnd device name\n\n");
+ printf(_2TABS" <bearer_id> int - bearer id\n\n");
+ printf(_2TABS" <flow id> int - flow id\n\n");
+ printf(_2TABS" <ip type> int - ip type\n\n");
+ printf("rmnetcli -n flowcontrol <real dev>");
+ printf(_2TABS" <vnd_name> string - vnd device name\n\n");
+ printf(_2TABS" <bearer_id> int - bearer id\n\n");
+ printf(_2TABS" <seq> int - sequence\n\n");
+ printf(_2TABS" <grant size> int - grant size\n\n");
+ printf(_2TABS" <ack> int - ack\n\n");
+ printf("rmnetcli -n systemup <real dev>\n");
+ printf(_2TABS" <vnd_name> string - vnd device name\n\n");
+ printf(_2TABS" <instance> int - bearer id\n\n");
+ printf(_2TABS" <eptype> int - ep type\n\n");
+ printf(_2TABS" <iface_id> int - iface id\n\n");
+ printf(_2TABS" <flags> int - flags\n\n");
+ printf("rmnetcli -n systemdown <real dev> <vnd name> <instance>\n\n ");
+
}
@@ -345,6 +370,72 @@
argv[2],
&error_number);
}
+ else if (!strcmp(*argv, "flowactivate")) {
+ _RMNETCLI_CHECKNULL(argv[1]);
+ _RMNETCLI_CHECKNULL(argv[2]);
+ _RMNETCLI_CHECKNULL(argv[3]);
+ _RMNETCLI_CHECKNULL(argv[4]);
+ _RMNETCLI_CHECKNULL(argv[5]);
+
+ return_code = rtrmnet_activate_flow(handle, argv[1], argv[2],
+ _STRTOUI8(argv[3]),
+ _STRTOI32(argv[4]),
+ _STRTOUI32(argv[5]),
+ _STRTOUI32(argv[6]),
+ &error_number);
+
+ } else if (!strcmp(*argv, "flowdel")) {
+ _RMNETCLI_CHECKNULL(argv[1]);
+ _RMNETCLI_CHECKNULL(argv[2]);
+ _RMNETCLI_CHECKNULL(argv[3]);
+ _RMNETCLI_CHECKNULL(argv[4]);
+ _RMNETCLI_CHECKNULL(argv[5]);
+ return_code = rtrmnet_delete_flow(handle, argv[1], argv[2],
+ _STRTOUI8(argv[3]),
+ _STRTOUI32(argv[4]),
+ _STRTOI32(argv[5]),
+ &error_number);
+ } else if (!strcmp(*argv, "flowcontrol")) {
+ _RMNETCLI_CHECKNULL(argv[1]);
+ _RMNETCLI_CHECKNULL(argv[2]);
+ _RMNETCLI_CHECKNULL(argv[3]);
+ _RMNETCLI_CHECKNULL(argv[4]);
+ _RMNETCLI_CHECKNULL(argv[5]);
+ _RMNETCLI_CHECKNULL(argv[6]);
+
+
+ return_code = rtrmnet_control_flow(handle, argv[1], argv[2],
+ _STRTOUI8(argv[3]),
+ _STRTOUI32(argv[4]),
+ _STRTOUI16(argv[5]),
+ _STRTOUI8(argv[6]),
+ &error_number);
+ } else if (!strcmp(*argv, "systemup")) {
+ _RMNETCLI_CHECKNULL(argv[1]);
+ _RMNETCLI_CHECKNULL(argv[2]);
+ _RMNETCLI_CHECKNULL(argv[3]);
+ _RMNETCLI_CHECKNULL(argv[4]);
+ _RMNETCLI_CHECKNULL(argv[5]);
+ _RMNETCLI_CHECKNULL(argv[6]);
+
+
+ return_code = rtrmnet_flow_state_up(handle, argv[1], argv[2],
+ _STRTOUI32(argv[3]),
+ _STRTOUI32(argv[4]),
+ _STRTOUI32(argv[5]),
+ _STRTOUI32(argv[6]),
+ &error_number);
+ } else if (!strcmp(*argv, "systemdown")) {
+ _RMNETCLI_CHECKNULL(argv[1]);
+ _RMNETCLI_CHECKNULL(argv[2]);
+ _RMNETCLI_CHECKNULL(argv[3]);
+
+ return_code = rtrmnet_flow_state_down(handle, argv[1], argv[2],
+ _STRTOUI32(argv[3]),
+ &error_number);
+ }
+
+
goto end;
} else {
return_code = rmnetctl_init(&handle, &error_number);
diff --git a/rmnetctl/inc/librmnetctl.h b/rmnetctl/inc/librmnetctl.h
index 3d622bf..7ecbcb9 100644
--- a/rmnetctl/inc/librmnetctl.h
+++ b/rmnetctl/inc/librmnetctl.h
@@ -600,5 +600,50 @@
int rtrmnet_ctl_bridgevnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname,
uint16_t *error_code);
+int rtrmnet_activate_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint32_t flow_id,
+ int ip_type,
+ uint32_t tcm_handle,
+ uint16_t *error_code);
+
+int rtrmnet_delete_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint32_t flow_id,
+ int ip_type,
+ uint16_t *error_code);
+
+
+
+
+int rtrmnet_control_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint16_t sequence,
+ uint32_t grantsize,
+ uint8_t ack,
+ uint16_t *error_code);
+
+int rtrmnet_flow_state_down(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint32_t instance,
+ uint16_t *error_code);
+
+
+int rtrmnet_flow_state_up(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint32_t instance,
+ uint32_t ep_type,
+ uint32_t ifaceid,
+ int flags,
+ uint16_t *error_code);
+
#endif /* not defined LIBRMNETCTL_H */
diff --git a/rmnetctl/src/librmnetctl.c b/rmnetctl/src/librmnetctl.c
index 731681a..b4dc543 100644
--- a/rmnetctl/src/librmnetctl.c
+++ b/rmnetctl/src/librmnetctl.c
@@ -1367,3 +1367,541 @@
return rmnet_get_ack(hndl, error_code);
}
+
+
+int rtrmnet_activate_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint32_t flow_id,
+ int ip_type,
+ uint32_t tcm_handle,
+ uint16_t *error_code)
+{
+ struct rtattr *attrinfo, *datainfo, *linkinfo;
+ struct tcmsg flowinfo;
+ char *kind = "rmnet";
+ struct nlmsg req;
+ int devindex = 0;
+ int val = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&flowinfo, 0, sizeof(flowinfo));
+
+
+ if (!hndl || !devname || !error_code)
+ return RMNETCTL_INVALID_ARG;
+
+ req.nl_addr.nlmsg_type = RTM_NEWLINK;
+ req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nl_addr.nlmsg_seq = hndl->transaction_id;
+ hndl->transaction_id++;
+
+ /* Get index of devname*/
+ devindex = if_nametoindex(devname);
+ if (devindex < 0) {
+ *error_code = errno;
+ return RMNETCTL_KERNEL_ERR;
+ }
+
+ /* Setup link attr with devindex as data */
+ val = devindex;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_LINK;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+ memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+ /* Set up IFLA info kind RMNET that has linkinfo and type */
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_IFNAME;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+ memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+ linkinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ linkinfo->rta_type = IFLA_LINKINFO;
+ linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_INFO_KIND;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+ memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+ datainfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ datainfo->rta_type = IFLA_INFO_DATA;
+ datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+ flowinfo.tcm_handle = tcm_handle;
+ flowinfo.tcm_family = 0x1;
+ flowinfo.tcm__pad1 = bearer_id;
+ flowinfo.tcm_ifindex = ip_type;
+ flowinfo.tcm_parent = flow_id;
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_VLAN_EGRESS_QOS;
+ attrinfo->rta_len = RTA_LENGTH(sizeof(flowinfo));
+ memcpy(RTA_DATA(attrinfo), &flowinfo, sizeof(flowinfo));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(flowinfo)));
+
+ datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+ linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+ if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+ *error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+ return RMNETCTL_LIB_ERR;
+ }
+
+ return rmnet_get_ack(hndl, error_code);
+}
+
+
+int rtrmnet_delete_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint32_t flow_id,
+ int ip_type,
+ uint16_t *error_code)
+{
+ struct rtattr *attrinfo, *datainfo, *linkinfo;
+ struct tcmsg flowinfo;
+ char *kind = "rmnet";
+ struct nlmsg req;
+ int devindex = 0;
+ int val = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&flowinfo, 0, sizeof(flowinfo));
+
+ if (!hndl || !devname || !error_code)
+ return RMNETCTL_INVALID_ARG;
+
+ req.nl_addr.nlmsg_type = RTM_NEWLINK;
+ req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nl_addr.nlmsg_seq = hndl->transaction_id;
+ hndl->transaction_id++;
+
+ /* Get index of devname*/
+ devindex = if_nametoindex(devname);
+ if (devindex < 0) {
+ *error_code = errno;
+ return RMNETCTL_KERNEL_ERR;
+ }
+
+ /* Setup link attr with devindex as data */
+ val = devindex;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_LINK;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+ memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+ /* Set up IFLA info kind RMNET that has linkinfo and type */
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_IFNAME;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+ memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+ linkinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ linkinfo->rta_type = IFLA_LINKINFO;
+ linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_INFO_KIND;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+ memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+ datainfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ datainfo->rta_type = IFLA_INFO_DATA;
+ datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+ flowinfo.tcm_family = 0x2;
+ flowinfo.tcm_ifindex = ip_type;
+ flowinfo.tcm__pad1 = bearer_id;
+ flowinfo.tcm_parent = flow_id;
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_VLAN_EGRESS_QOS;
+ attrinfo->rta_len = RTA_LENGTH(sizeof(flowinfo));
+ memcpy(RTA_DATA(attrinfo), &flowinfo, sizeof(flowinfo));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(flowinfo)));
+
+
+ datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+
+ linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+ if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+ *error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+ return RMNETCTL_LIB_ERR;
+ }
+
+ return rmnet_get_ack(hndl, error_code);
+}
+
+int rtrmnet_control_flow(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint8_t bearer_id,
+ uint16_t sequence,
+ uint32_t grantsize,
+ uint8_t ack,
+ uint16_t *error_code)
+{
+ struct rtattr *attrinfo, *datainfo, *linkinfo;
+ struct tcmsg flowinfo;
+ char *kind = "rmnet";
+ struct nlmsg req;
+ int devindex = 0;
+ int val = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&flowinfo, 0, sizeof(flowinfo));
+
+
+ if (!hndl || !devname || !error_code)
+ return RMNETCTL_INVALID_ARG;
+
+ req.nl_addr.nlmsg_type = RTM_NEWLINK;
+ req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nl_addr.nlmsg_seq = hndl->transaction_id;
+ hndl->transaction_id++;
+
+ /* Get index of devname*/
+ devindex = if_nametoindex(devname);
+ if (devindex < 0) {
+ *error_code = errno;
+ return RMNETCTL_KERNEL_ERR;
+ }
+
+ /* Setup link attr with devindex as data */
+ val = devindex;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_LINK;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+ memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+ /* Set up IFLA info kind RMNET that has linkinfo and type */
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_IFNAME;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+ memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+ linkinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ linkinfo->rta_type = IFLA_LINKINFO;
+ linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_INFO_KIND;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+ memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+ datainfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ datainfo->rta_type = IFLA_INFO_DATA;
+ datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ flowinfo.tcm_family = 0x3;
+ flowinfo.tcm__pad1 = bearer_id;
+ flowinfo.tcm__pad2 = sequence;
+ flowinfo.tcm_parent = ack;
+ flowinfo.tcm_info = grantsize;
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_VLAN_EGRESS_QOS;
+ attrinfo->rta_len = RTA_LENGTH(sizeof(flowinfo));
+ memcpy(RTA_DATA(attrinfo), &flowinfo, sizeof(flowinfo));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(flowinfo)));
+
+ datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+ linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+ if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+ *error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+ return RMNETCTL_LIB_ERR;
+ }
+
+ return rmnet_get_ack(hndl, error_code);
+}
+
+
+int rtrmnet_flow_state_up(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint32_t instance,
+ uint32_t ep_type,
+ uint32_t ifaceid,
+ int flags,
+ uint16_t *error_code)
+{
+ struct rtattr *attrinfo, *datainfo, *linkinfo;
+ struct tcmsg flowinfo;
+ char *kind = "rmnet";
+ struct nlmsg req;
+ int devindex = 0;
+ int val = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&flowinfo, 0, sizeof(flowinfo));
+
+
+ if (!hndl || !devname || !error_code)
+ return RMNETCTL_INVALID_ARG;
+
+ req.nl_addr.nlmsg_type = RTM_NEWLINK;
+ req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nl_addr.nlmsg_seq = hndl->transaction_id;
+ hndl->transaction_id++;
+
+ /* Get index of devname*/
+ devindex = if_nametoindex(devname);
+ if (devindex < 0) {
+ *error_code = errno;
+ return RMNETCTL_KERNEL_ERR;
+ }
+
+ /* Setup link attr with devindex as data */
+ val = devindex;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_LINK;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+ memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+ /* Set up IFLA info kind RMNET that has linkinfo and type */
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_IFNAME;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+ memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+ linkinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ linkinfo->rta_type = IFLA_LINKINFO;
+ linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_INFO_KIND;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+ memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+ datainfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ datainfo->rta_type = IFLA_INFO_DATA;
+ datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+ flowinfo.tcm_handle = instance;
+ flowinfo.tcm_family = 0x4;
+ flowinfo.tcm_ifindex = flags;
+ flowinfo.tcm_parent = ifaceid;
+ flowinfo.tcm_info = ep_type;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_VLAN_EGRESS_QOS;
+ attrinfo->rta_len = RTA_LENGTH(sizeof(flowinfo));
+ memcpy(RTA_DATA(attrinfo), &flowinfo, sizeof(flowinfo));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(flowinfo)));
+
+ datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+ linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+ if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+ *error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+ return RMNETCTL_LIB_ERR;
+ }
+
+ return rmnet_get_ack(hndl, error_code);
+}
+
+
+int rtrmnet_flow_state_down(rmnetctl_hndl_t *hndl,
+ char *devname,
+ char *vndname,
+ uint32_t instance,
+ uint16_t *error_code)
+{
+ struct rtattr *attrinfo, *datainfo, *linkinfo;
+ struct tcmsg flowinfo;
+ char *kind = "rmnet";
+ struct nlmsg req;
+ int devindex = 0;
+ int val = 0;
+
+ memset(&req, 0, sizeof(req));
+ memset(&flowinfo, 0, sizeof(flowinfo));
+
+
+ if (!hndl || !devname || !error_code)
+ return RMNETCTL_INVALID_ARG;
+
+ req.nl_addr.nlmsg_type = RTM_NEWLINK;
+ req.nl_addr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.nl_addr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ req.nl_addr.nlmsg_seq = hndl->transaction_id;
+ hndl->transaction_id++;
+
+ /* Get index of devname*/
+ devindex = if_nametoindex(devname);
+ if (devindex < 0) {
+ *error_code = errno;
+ return RMNETCTL_KERNEL_ERR;
+ }
+
+ /* Setup link attr with devindex as data */
+ val = devindex;
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_LINK;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+ memcpy(RTA_DATA(attrinfo), &val, sizeof(val));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(val)));
+
+ /* Set up IFLA info kind RMNET that has linkinfo and type */
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_IFNAME;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+ memcpy(RTA_DATA(attrinfo), vndname, strlen(vndname) + 1);
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(vndname) + 1));
+
+ linkinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ linkinfo->rta_type = IFLA_LINKINFO;
+ linkinfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ attrinfo->rta_type = IFLA_INFO_KIND;
+ attrinfo->rta_len = RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+ memcpy(RTA_DATA(attrinfo), kind, strlen(kind));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(strlen(kind)));
+
+ datainfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+
+ datainfo->rta_type = IFLA_INFO_DATA;
+ datainfo->rta_len = RTA_ALIGN(RTA_LENGTH(0));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(0));
+
+ flowinfo.tcm_handle = instance;
+ flowinfo.tcm_family = 0x5;
+
+ attrinfo = (struct rtattr *)(((char *)&req) +
+ NLMSG_ALIGN(req.nl_addr.nlmsg_len));
+ attrinfo->rta_type = IFLA_VLAN_EGRESS_QOS;
+ attrinfo->rta_len = RTA_LENGTH(sizeof(flowinfo));
+ memcpy(RTA_DATA(attrinfo), &flowinfo, sizeof(flowinfo));
+ req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) +
+ RTA_ALIGN(RTA_LENGTH(sizeof(flowinfo)));
+
+
+ datainfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)datainfo;
+ linkinfo->rta_len = (char *)NLMSG_TAIL(&req.nl_addr) - (char *)linkinfo;
+
+ if (send(hndl->netlink_fd, &req, req.nl_addr.nlmsg_len, 0) < 0) {
+ *error_code = RMNETCTL_API_ERR_MESSAGE_SEND;
+ return RMNETCTL_LIB_ERR;
+ }
+
+ return rmnet_get_ack(hndl, error_code);
+}