//https://source.codeaurora.org/quic/la/platform/vendor/qcom-opensource/dataservices/tree/rmnetctl #include #include #include #include #include #include #include #include #include #include #include #include #include #define RMNETCTL_SUCCESS 0 #define RMNETCTL_LIB_ERR 1 #define RMNETCTL_KERNEL_ERR 2 #define RMNETCTL_INVALID_ARG 3 enum rmnetctl_error_codes_e { RMNETCTL_API_SUCCESS = 0, RMNETCTL_API_FIRST_ERR = 1, RMNETCTL_API_ERR_MESSAGE_SEND = 3, RMNETCTL_API_ERR_MESSAGE_RECEIVE = 4, RMNETCTL_INIT_FIRST_ERR = 5, RMNETCTL_INIT_ERR_PROCESS_ID = RMNETCTL_INIT_FIRST_ERR, RMNETCTL_INIT_ERR_NETLINK_FD = 6, RMNETCTL_INIT_ERR_BIND = 7, RMNETCTL_API_SECOND_ERR = 9, RMNETCTL_API_ERR_HNDL_INVALID = RMNETCTL_API_SECOND_ERR, RMNETCTL_API_ERR_RETURN_TYPE = 13, }; struct rmnetctl_hndl_s { uint32_t pid; uint32_t transaction_id; int netlink_fd; struct sockaddr_nl src_addr, dest_addr; }; typedef struct rmnetctl_hndl_s rmnetctl_hndl_t; #define NLMSG_TAIL(nmsg) \ ((struct rtattr *) (((char *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) struct nlmsg { struct nlmsghdr nl_addr; struct ifinfomsg ifmsg; char data[500]; }; #define MIN_VALID_PROCESS_ID 0 #define MIN_VALID_SOCKET_FD 0 #define KERNEL_PROCESS_ID 0 #define UNICAST 0 enum { IFLA_RMNET_UL_AGG_PARAMS = __IFLA_RMNET_MAX, __IFLA_RMNET_EXT_MAX, }; struct rmnet_egress_agg_params { uint16_t agg_size; uint16_t agg_count; uint32_t agg_time; }; static int rmnet_get_ack(rmnetctl_hndl_t *hndl, uint16_t *error_code) { struct nlack { struct nlmsghdr ackheader; struct nlmsgerr ackdata; char data[256]; } ack; int i; if (!hndl || !error_code) return RMNETCTL_INVALID_ARG; if ((i = recv(hndl->netlink_fd, &ack, sizeof(ack), 0)) < 0) { *error_code = errno; return RMNETCTL_API_ERR_MESSAGE_RECEIVE; } /*Ack should always be NLMSG_ERROR type*/ if (ack.ackheader.nlmsg_type == NLMSG_ERROR) { if (ack.ackdata.error == 0) { *error_code = RMNETCTL_API_SUCCESS; return RMNETCTL_SUCCESS; } else { *error_code = -ack.ackdata.error; return RMNETCTL_KERNEL_ERR; } } *error_code = RMNETCTL_API_ERR_RETURN_TYPE; return RMNETCTL_API_FIRST_ERR; } static int rtrmnet_ctl_init(rmnetctl_hndl_t **hndl, uint16_t *error_code) { struct sockaddr_nl __attribute__((__may_alias__)) *saddr_ptr; int netlink_fd = -1; pid_t pid = 0; if (!hndl || !error_code) return RMNETCTL_INVALID_ARG; *hndl = (rmnetctl_hndl_t *)malloc(sizeof(rmnetctl_hndl_t)); if (!*hndl) { *error_code = RMNETCTL_API_ERR_HNDL_INVALID; return RMNETCTL_LIB_ERR; } memset(*hndl, 0, sizeof(rmnetctl_hndl_t)); pid = getpid(); if (pid < MIN_VALID_PROCESS_ID) { free(*hndl); *error_code = RMNETCTL_INIT_ERR_PROCESS_ID; return RMNETCTL_LIB_ERR; } (*hndl)->pid = KERNEL_PROCESS_ID; netlink_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (netlink_fd < MIN_VALID_SOCKET_FD) { free(*hndl); *error_code = RMNETCTL_INIT_ERR_NETLINK_FD; return RMNETCTL_LIB_ERR; } (*hndl)->netlink_fd = netlink_fd; memset(&(*hndl)->src_addr, 0, sizeof(struct sockaddr_nl)); (*hndl)->src_addr.nl_family = AF_NETLINK; (*hndl)->src_addr.nl_pid = (*hndl)->pid; saddr_ptr = &(*hndl)->src_addr; if (bind((*hndl)->netlink_fd, (struct sockaddr *)saddr_ptr, sizeof(struct sockaddr_nl)) < 0) { close((*hndl)->netlink_fd); free(*hndl); *error_code = RMNETCTL_INIT_ERR_BIND; return RMNETCTL_LIB_ERR; } memset(&(*hndl)->dest_addr, 0, sizeof(struct sockaddr_nl)); (*hndl)->dest_addr.nl_family = AF_NETLINK; (*hndl)->dest_addr.nl_pid = KERNEL_PROCESS_ID; (*hndl)->dest_addr.nl_groups = UNICAST; return RMNETCTL_SUCCESS; } static int rtrmnet_ctl_deinit(rmnetctl_hndl_t *hndl) { if (!hndl) return RMNETCTL_SUCCESS; close(hndl->netlink_fd); free(hndl); return RMNETCTL_SUCCESS; } static int rtrmnet_ctl_newvnd(rmnetctl_hndl_t *hndl, char *devname, char *vndname, uint16_t *error_code, uint8_t index, uint32_t flagconfig, uint32_t ul_agg_cnt, uint32_t ul_agg_size) { struct rtattr *attrinfo, *datainfo, *linkinfo; struct ifla_vlan_flags flags; int devindex = 0, val = 0; char *kind = "rmnet"; struct nlmsg req; short id; if (!hndl || !devname || !vndname || !error_code) return RMNETCTL_INVALID_ARG; memset(&req, 0, sizeof(req)); 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_CREATE | NLM_F_EXCL | 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)); id = index; attrinfo = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); attrinfo->rta_type = IFLA_VLAN_ID; attrinfo->rta_len = RTA_LENGTH(sizeof(id)); memcpy(RTA_DATA(attrinfo), &id, sizeof(id)); req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + RTA_ALIGN(RTA_LENGTH(sizeof(id))); if (flagconfig != 0) { flags.mask = flagconfig; flags.flags = flagconfig; attrinfo = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); attrinfo->rta_type = IFLA_VLAN_FLAGS; attrinfo->rta_len = RTA_LENGTH(sizeof(flags)); memcpy(RTA_DATA(attrinfo), &flags, sizeof(flags)); req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + RTA_ALIGN(RTA_LENGTH(sizeof(flags))); } if (ul_agg_cnt > 1) { struct rmnet_egress_agg_params agg_params; agg_params.agg_size = ul_agg_size; agg_params.agg_count = ul_agg_cnt; agg_params.agg_time = 3000000; attrinfo = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.nl_addr.nlmsg_len)); attrinfo->rta_type = IFLA_RMNET_UL_AGG_PARAMS; attrinfo->rta_len = RTA_LENGTH(sizeof(agg_params)); memcpy(RTA_DATA(attrinfo), &agg_params, sizeof(agg_params)); req.nl_addr.nlmsg_len = NLMSG_ALIGN(req.nl_addr.nlmsg_len) + RTA_ALIGN(RTA_LENGTH(sizeof(agg_params))); } 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_ctl_create_vnd(char *devname, char *vndname, uint8_t muxid, uint32_t qmap_version, uint32_t ul_agg_cnt, uint32_t ul_agg_size) { struct rmnetctl_hndl_s *handle; uint16_t error_code; int return_code; uint32_t flagconfig = RMNET_FLAGS_INGRESS_DEAGGREGATION; printf("%s devname: %s, vndname: %s, muxid: %d, qmap_version: %d\n", __func__, devname, vndname, muxid, qmap_version); ul_agg_cnt = 0; //TODO if (ul_agg_cnt > 1) flagconfig |= RMNET_EGRESS_FORMAT_AGGREGATION; if (qmap_version == 9) { //QMAPV5 #ifdef RMNET_FLAGS_INGRESS_MAP_CKSUMV5 flagconfig |= RMNET_FLAGS_INGRESS_MAP_CKSUMV5; flagconfig |= RMNET_FLAGS_EGRESS_MAP_CKSUMV5; #else return -1001; #endif } else if (qmap_version == 8) { //QMAPV4 flagconfig |= RMNET_FLAGS_INGRESS_MAP_CKSUMV4; flagconfig |= RMNET_FLAGS_EGRESS_MAP_CKSUMV4; } else if (qmap_version == 5) { //QMAPV1 } else { flagconfig = 0; } return_code = rtrmnet_ctl_init(&handle, &error_code); if (return_code) { printf("rtrmnet_ctl_init error_code: %d, return_code: %d, errno: %d (%s)\n", error_code, return_code, errno, strerror(errno)); } if (return_code == RMNETCTL_SUCCESS) { return_code = rtrmnet_ctl_newvnd(handle, devname, vndname, &error_code, muxid, flagconfig, ul_agg_cnt, ul_agg_size); if (return_code) { printf("rtrmnet_ctl_newvnd error_code: %d, return_code: %d, errno: %d (%s)\n", error_code, return_code, errno, strerror(errno)); } rtrmnet_ctl_deinit(handle); } return return_code; }