mirror of
https://github.com/coolsnowwolf/lede.git
synced 2025-04-16 04:13:31 +00:00
WWAN: add Fibocom linux usb QMI WWAN driver and dial app
This commit is contained in:
parent
15d1af2d95
commit
012f1419de
27
package/wwan/app/fibocom-dial/Makefile
Executable file
27
package/wwan/app/fibocom-dial/Makefile
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=fibocom-dial
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/fibocom-dial
|
||||||
|
SECTION:=utils
|
||||||
|
CATEGORY:=Utilities
|
||||||
|
TITLE:=Fibocom Dial
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/fibocom-dial/description
|
||||||
|
Fibocom Dial (Prints a snarky message)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/fibocom-dial/install
|
||||||
|
$(INSTALL_DIR) $(1)/bin
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibocom-dial $(1)/bin/
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fibo_qmimsg_server $(1)/bin/
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/multi-pdn-manager $(1)/bin/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,fibocom-dial))
|
275
package/wwan/app/fibocom-dial/src/GobiNetCM.c
Executable file
275
package/wwan/app/fibocom-dial/src/GobiNetCM.c
Executable file
@ -0,0 +1,275 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#include "QMIThread.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_GOBINET
|
||||||
|
|
||||||
|
// IOCTL to generate a client ID for this service type
|
||||||
|
#define IOCTL_QMI_GET_SERVICE_FILE 0x8BE0 + 1
|
||||||
|
|
||||||
|
// IOCTL to get the VIDPID of the device
|
||||||
|
#define IOCTL_QMI_GET_DEVICE_VIDPID 0x8BE0 + 2
|
||||||
|
|
||||||
|
// IOCTL to get the MEID of the device
|
||||||
|
#define IOCTL_QMI_GET_DEVICE_MEID 0x8BE0 + 3
|
||||||
|
|
||||||
|
static int GobiNetSendQMI(PQCQMIMSG pRequest)
|
||||||
|
{
|
||||||
|
int ret, fd;
|
||||||
|
|
||||||
|
static int send_count = 0;
|
||||||
|
fd = qmiclientId[pRequest->QMIHdr.QMIType];
|
||||||
|
|
||||||
|
if (fd <= 0) {
|
||||||
|
dbg_time("%s QMIType: %d has no clientID", __func__,
|
||||||
|
pRequest->QMIHdr.QMIType);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always ready to write
|
||||||
|
re_write:
|
||||||
|
if (1 == 1) {
|
||||||
|
ssize_t nwrites =
|
||||||
|
le16_to_cpu(pRequest->QMIHdr.Length) + 1 - sizeof(QCQMI_HDR);
|
||||||
|
ret = write(fd, &pRequest->MUXMsg, nwrites);
|
||||||
|
if (ret == nwrites)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
send_count = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
send_count++;
|
||||||
|
dbg_time("%s write=%d, errno: %d (%s) send_count %d", __func__, ret, errno, strerror(errno), send_count);
|
||||||
|
if (send_count < 3)
|
||||||
|
{
|
||||||
|
sleep(1);
|
||||||
|
goto re_write;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GobiNetGetClientID(const char *qcqmi, UCHAR QMIType)
|
||||||
|
{
|
||||||
|
int ClientId;
|
||||||
|
ClientId = open(qcqmi, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
if (ClientId == -1) {
|
||||||
|
dbg_time("failed to open %s, errno: %d (%s)", qcqmi, errno,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(ClientId, IOCTL_QMI_GET_SERVICE_FILE, QMIType) != 0) {
|
||||||
|
dbg_time("failed to get ClientID for 0x%02x errno: %d (%s)", QMIType,
|
||||||
|
errno, strerror(errno));
|
||||||
|
close(ClientId);
|
||||||
|
ClientId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId);
|
||||||
|
|
||||||
|
switch (QMIType) {
|
||||||
|
case QMUX_TYPE_WDS:
|
||||||
|
dbg_time("Get clientWDS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_DMS:
|
||||||
|
dbg_time("Get clientDMS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_NAS:
|
||||||
|
dbg_time("Get clientNAS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_QOS:
|
||||||
|
dbg_time("Get clientQOS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WMS:
|
||||||
|
dbg_time("Get clientWMS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_PDS:
|
||||||
|
dbg_time("Get clientPDS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_UIM:
|
||||||
|
dbg_time("Get clientUIM = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WDS_ADMIN:
|
||||||
|
dbg_time("Get clientWDA = %d", ClientId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GobiNetDeInit(void)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
|
||||||
|
if (qmiclientId[i] != 0) {
|
||||||
|
close(qmiclientId[i]);
|
||||||
|
qmiclientId[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static void *GobiNetThread(void *pData)
|
||||||
|
{
|
||||||
|
PROFILE_T *profile = (PROFILE_T *)pData;
|
||||||
|
const char *qcqmi = (const char *)profile->qmichannel;
|
||||||
|
int wait_for_request_quit = 0;
|
||||||
|
dbg_time("%s %d", __func__, __LINE__);
|
||||||
|
if (profile->ipv4_flag)
|
||||||
|
qmiclientId[QMUX_TYPE_WDS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
|
||||||
|
if (profile->ipv6_flag)
|
||||||
|
qmiclientId[QMUX_TYPE_WDS_IPV6] = GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS);
|
||||||
|
qmiclientId[QMUX_TYPE_DMS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_DMS);
|
||||||
|
qmiclientId[QMUX_TYPE_NAS] = GobiNetGetClientID(qcqmi, QMUX_TYPE_NAS);
|
||||||
|
qmiclientId[QMUX_TYPE_UIM] = GobiNetGetClientID(qcqmi, QMUX_TYPE_UIM);
|
||||||
|
// qmiclientId[QMUX_TYPE_WDS_ADMIN] =
|
||||||
|
// GobiNetGetClientID(qcqmi, QMUX_TYPE_WDS_ADMIN);
|
||||||
|
|
||||||
|
//if ((qmiclientId[QMUX_TYPE_WDS] == 0) && (qmiclientId[QMUX_TYPE_WDS_IPV6] == 0)) /*|| (clientWDA == -1)*/ {
|
||||||
|
|
||||||
|
if ((qmiclientId[QMUX_TYPE_DMS] == 0) ||
|
||||||
|
(qmiclientId[QMUX_TYPE_NAS] == 0) ||
|
||||||
|
(qmiclientId[QMUX_TYPE_UIM] == 0) ||
|
||||||
|
(profile->ipv4_flag ? ((qmiclientId[QMUX_TYPE_WDS] == 0) ? 1 : 0):0)||
|
||||||
|
(profile->ipv6_flag ? ((qmiclientId[QMUX_TYPE_WDS_IPV6] == 0) ? 1 : 0):0))
|
||||||
|
{
|
||||||
|
GobiNetDeInit();
|
||||||
|
dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, qcqmi, errno,strerror(errno));
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct pollfd pollfds[16] = {{qmidevice_control_fd[1], POLLIN, 0}};
|
||||||
|
int ne, ret, nevents = 1;
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
|
||||||
|
if (qmiclientId[i] != 0) {
|
||||||
|
pollfds[nevents].fd = qmiclientId[i];
|
||||||
|
pollfds[nevents].events = POLLIN;
|
||||||
|
pollfds[nevents].revents = 0;
|
||||||
|
nevents++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
|
||||||
|
} while ((ret < 0) && (errno == EINTR));
|
||||||
|
|
||||||
|
if (ret == 0 && wait_for_request_quit) {
|
||||||
|
QmiThreadRecvQMI(
|
||||||
|
NULL); // main thread may pending on QmiThreadSendQMI()
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret <= 0) {
|
||||||
|
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
|
||||||
|
strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ne = 0; ne < nevents; ne++) {
|
||||||
|
int fd = pollfds[ne].fd;
|
||||||
|
short revents = pollfds[ne].revents;
|
||||||
|
|
||||||
|
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||||
|
dbg_time("%s poll err/hup/inval", __func__);
|
||||||
|
dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
|
||||||
|
if (fd == qmidevice_control_fd[1]) {
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
if (revents & (POLLERR | POLLHUP | POLLNVAL))
|
||||||
|
goto __GobiNetThread_quit;
|
||||||
|
}
|
||||||
|
if ((revents & POLLIN) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fd == qmidevice_control_fd[1]) {
|
||||||
|
int triger_event;
|
||||||
|
if (read(fd, &triger_event, sizeof(triger_event)) ==
|
||||||
|
sizeof(triger_event)) {
|
||||||
|
// DBG("triger_event = 0x%x", triger_event);
|
||||||
|
switch (triger_event) {
|
||||||
|
case RIL_REQUEST_QUIT:
|
||||||
|
goto __GobiNetThread_quit;
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
wait_for_request_quit = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ssize_t nreads;
|
||||||
|
static UCHAR QMIBuf[4096];
|
||||||
|
PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
|
||||||
|
|
||||||
|
nreads = read(fd, &pResponse->MUXMsg,
|
||||||
|
sizeof(QMIBuf) - sizeof(QCQMI_HDR));
|
||||||
|
if (nreads <= 0) {
|
||||||
|
dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
|
||||||
|
errno, strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]);
|
||||||
|
i++) {
|
||||||
|
if (qmiclientId[i] == fd) {
|
||||||
|
pResponse->QMIHdr.QMIType = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pResponse->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
|
||||||
|
pResponse->QMIHdr.Length =
|
||||||
|
cpu_to_le16(nreads + sizeof(QCQMI_HDR) - 1);
|
||||||
|
pResponse->QMIHdr.CtlFlags = 0x00;
|
||||||
|
pResponse->QMIHdr.ClientId = fd & 0xFF;
|
||||||
|
|
||||||
|
QmiThreadRecvQMI(pResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__GobiNetThread_quit:
|
||||||
|
GobiNetDeInit();
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||||
|
QmiThreadRecvQMI(NULL); // main thread may pending on QmiThreadSendQMI()
|
||||||
|
dbg_time("%s exit", __func__);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
static int GobiNetSendQMI(PQCQMIMSG pRequest) { return -1; }
|
||||||
|
static void *GobiNetThread(void *pData)
|
||||||
|
{
|
||||||
|
dbg_time("please set CONFIG_GOBINET");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct qmi_device_ops gobi_qmidev_ops = {
|
||||||
|
.deinit = GobiNetDeInit,
|
||||||
|
.send = GobiNetSendQMI,
|
||||||
|
.read = GobiNetThread,
|
||||||
|
};
|
363
package/wwan/app/fibocom-dial/src/MPQCTL.h
Executable file
363
package/wwan/app/fibocom-dial/src/MPQCTL.h
Executable file
@ -0,0 +1,363 @@
|
|||||||
|
/*===========================================================================
|
||||||
|
|
||||||
|
M P Q C T L. H
|
||||||
|
DESCRIPTION:
|
||||||
|
|
||||||
|
This module contains QMI QCTL module.
|
||||||
|
|
||||||
|
INITIALIZATION AND SEQUENCING REQUIREMENTS:
|
||||||
|
|
||||||
|
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
|
||||||
|
===========================================================================*/
|
||||||
|
|
||||||
|
#ifndef MPQCTL_H
|
||||||
|
#define MPQCTL_H
|
||||||
|
|
||||||
|
#include "MPQMI.h"
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
// ================= QMICTL ==================
|
||||||
|
|
||||||
|
// QMICTL Control Flags
|
||||||
|
#define QMICTL_CTL_FLAG_CMD 0x00
|
||||||
|
#define QMICTL_CTL_FLAG_RSP 0x01
|
||||||
|
#define QMICTL_CTL_FLAG_IND 0x02
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
typedef struct _QMICTL_TRANSACTION_ITEM
|
||||||
|
{
|
||||||
|
LIST_ENTRY List;
|
||||||
|
UCHAR TransactionId; // QMICTL transaction id
|
||||||
|
PVOID Context; // Adapter or IocDev
|
||||||
|
PIRP Irp;
|
||||||
|
} QMICTL_TRANSACTION_ITEM, *PQMICTL_TRANSACTION_ITEM;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct _QCQMICTL_MSG_HDR {
|
||||||
|
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType;
|
||||||
|
USHORT Length;
|
||||||
|
} __attribute__((packed)) QCQMICTL_MSG_HDR, *PQCQMICTL_MSG_HDR;
|
||||||
|
|
||||||
|
#define QCQMICTL_MSG_HDR_SIZE sizeof(QCQMICTL_MSG_HDR)
|
||||||
|
|
||||||
|
typedef struct _QCQMICTL_MSG_HDR_RESP {
|
||||||
|
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // 0x02 - result code
|
||||||
|
USHORT TLVLength; // 4
|
||||||
|
USHORT QMUXResult; // QMI_RESULT_SUCCESS
|
||||||
|
// QMI_RESULT_FAILURE
|
||||||
|
USHORT QMUXError; // QMI_ERR_INVALID_ARG
|
||||||
|
// QMI_ERR_NO_MEMORY
|
||||||
|
// QMI_ERR_INTERNAL
|
||||||
|
// QMI_ERR_FAULT
|
||||||
|
} __attribute__((packed)) QCQMICTL_MSG_HDR_RESP, *PQCQMICTL_MSG_HDR_RESP;
|
||||||
|
|
||||||
|
typedef struct _QCQMICTL_MSG {
|
||||||
|
UCHAR CtlFlags; // 00-cmd, 01-rsp, 10-ind
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR Payload;
|
||||||
|
} __attribute__((packed)) QCQMICTL_MSG, *PQCQMICTL_MSG;
|
||||||
|
|
||||||
|
// TLV Header
|
||||||
|
typedef struct _QCQMICTL_TLV_HDR {
|
||||||
|
UCHAR TLVType;
|
||||||
|
USHORT TLVLength;
|
||||||
|
} __attribute__((packed)) QCQMICTL_TLV_HDR, *PQCQMICTL_TLV_HDR;
|
||||||
|
|
||||||
|
#define QCQMICTL_TLV_HDR_SIZE sizeof(QCQMICTL_TLV_HDR)
|
||||||
|
|
||||||
|
// QMICTL Type
|
||||||
|
#define QMICTL_SET_INSTANCE_ID_REQ 0x0020
|
||||||
|
#define QMICTL_SET_INSTANCE_ID_RESP 0x0020
|
||||||
|
#define QMICTL_GET_VERSION_REQ 0x0021
|
||||||
|
#define QMICTL_GET_VERSION_RESP 0x0021
|
||||||
|
#define QMICTL_GET_CLIENT_ID_REQ 0x0022
|
||||||
|
#define QMICTL_GET_CLIENT_ID_RESP 0x0022
|
||||||
|
#define QMICTL_RELEASE_CLIENT_ID_REQ 0x0023
|
||||||
|
#define QMICTL_RELEASE_CLIENT_ID_RESP 0x0023
|
||||||
|
#define QMICTL_REVOKE_CLIENT_ID_IND 0x0024
|
||||||
|
#define QMICTL_INVALID_CLIENT_ID_IND 0x0025
|
||||||
|
#define QMICTL_SET_DATA_FORMAT_REQ 0x0026
|
||||||
|
#define QMICTL_SET_DATA_FORMAT_RESP 0x0026
|
||||||
|
#define QMICTL_SYNC_REQ 0x0027
|
||||||
|
#define QMICTL_SYNC_RESP 0x0027
|
||||||
|
#define QMICTL_SYNC_IND 0x0027
|
||||||
|
|
||||||
|
#define QMICTL_FLAG_REQUEST 0x00
|
||||||
|
#define QMICTL_FLAG_RESPONSE 0x01
|
||||||
|
#define QMICTL_FLAG_INDICATION 0x02
|
||||||
|
|
||||||
|
// QMICTL Message Definitions
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SET_INSTANCE_ID_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_REQ
|
||||||
|
USHORT Length; // 4
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR Value; // Host-unique QMI instance for this device driver
|
||||||
|
} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_REQ_MSG,
|
||||||
|
*PQMICTL_SET_INSTANCE_ID_REQ_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SET_INSTANCE_ID_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_SET_INSTANCE_ID_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult;
|
||||||
|
USHORT QMIError;
|
||||||
|
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLV2Length; // 0x0002
|
||||||
|
USHORT QMI_ID; // Upper byte is assigned by MSM,
|
||||||
|
// lower assigned by host
|
||||||
|
} __attribute__((packed)) QMICTL_SET_INSTANCE_ID_RESP_MSG,
|
||||||
|
*PQMICTL_SET_INSTANCE_ID_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_GET_VERSION_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_GET_VERSION_REQ
|
||||||
|
USHORT Length; // 0
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // var
|
||||||
|
UCHAR QMUXTypes; // List of one byte QMUX_TYPE values
|
||||||
|
// 0xFF returns a list of versions for all
|
||||||
|
// QMUX_TYPEs implemented on the device
|
||||||
|
} __attribute__((packed)) QMICTL_GET_VERSION_REQ_MSG,
|
||||||
|
*PQMICTL_GET_VERSION_REQ_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMUX_TYPE_VERSION_STRUCT {
|
||||||
|
UCHAR QMUXType;
|
||||||
|
USHORT MajorVersion;
|
||||||
|
USHORT MinorVersion;
|
||||||
|
} __attribute__((packed)) QMUX_TYPE_VERSION_STRUCT, *PQMUX_TYPE_VERSION_STRUCT;
|
||||||
|
|
||||||
|
typedef struct _ADDENDUM_VERSION_PREAMBLE {
|
||||||
|
UCHAR LabelLength;
|
||||||
|
UCHAR Label;
|
||||||
|
} __attribute__((packed)) ADDENDUM_VERSION_PREAMBLE,
|
||||||
|
*PADDENDUM_VERSION_PREAMBLE;
|
||||||
|
|
||||||
|
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_VERSION 0x01
|
||||||
|
#define QMICTL_GET_VERSION_RSP_TLV_TYPE_ADD_VERSION 0x10
|
||||||
|
|
||||||
|
typedef struct _QMICTL_GET_VERSION_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_GET_VERSION_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult;
|
||||||
|
USHORT QMIError;
|
||||||
|
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLV2Length; // var
|
||||||
|
UCHAR NumElements; // Num of QMUX_TYPE_VERSION_STRUCT
|
||||||
|
QMUX_TYPE_VERSION_STRUCT TypeVersion[0];
|
||||||
|
} __attribute__((packed)) QMICTL_GET_VERSION_RESP_MSG,
|
||||||
|
*PQMICTL_GET_VERSION_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_GET_CLIENT_ID_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_REQ
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR QMIType; // QMUX type
|
||||||
|
} __attribute__((packed)) QMICTL_GET_CLIENT_ID_REQ_MSG,
|
||||||
|
*PQMICTL_GET_CLIENT_ID_REQ_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_GET_CLIENT_ID_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_GET_CLIENT_ID_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult; // result code
|
||||||
|
USHORT QMIError; // error code
|
||||||
|
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLV2Length; // 2
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QMICTL_GET_CLIENT_ID_RESP_MSG,
|
||||||
|
*PQMICTL_GET_CLIENT_ID_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_RELEASE_CLIENT_ID_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_REQ
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 0x0002
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_REQ_MSG,
|
||||||
|
*PQMICTL_RELEASE_CLIENT_ID_REQ_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_RELEASE_CLIENT_ID_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_RELEASE_CLIENT_ID_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult; // result code
|
||||||
|
USHORT QMIError; // error code
|
||||||
|
UCHAR TLV2Type; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLV2Length; // 2
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QMICTL_RELEASE_CLIENT_ID_RESP_MSG,
|
||||||
|
*PQMICTL_RELEASE_CLIENT_ID_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_REVOKE_CLIENT_ID_IND_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 0x0002
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QMICTL_REVOKE_CLIENT_ID_IND_MSG,
|
||||||
|
*PQMICTL_REVOKE_CLIENT_ID_IND_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_INVALID_CLIENT_ID_IND_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 0x0002
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QMICTL_INVALID_CLIENT_ID_IND_MSG,
|
||||||
|
*PQMICTL_INVALID_CLIENT_ID_IND_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_REQ
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_REQUIRED_PARAMETER
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR DataFormat; // 0-default; 1-QoS hdr present
|
||||||
|
} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_REQ_MSG,
|
||||||
|
*PQMICTL_SET_DATA_FORMAT_REQ_MSG;
|
||||||
|
|
||||||
|
#ifdef QC_IP_MODE
|
||||||
|
#define SET_DATA_FORMAT_TLV_TYPE_LINK_PROTO 0x10
|
||||||
|
#define SET_DATA_FORMAT_LINK_PROTO_ETH 0x0001
|
||||||
|
#define SET_DATA_FORMAT_LINK_PROTO_IP 0x0002
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT {
|
||||||
|
UCHAR TLVType; // Link-Layer Protocol
|
||||||
|
USHORT TLVLength; // 2
|
||||||
|
USHORT LinkProt; // 0x1: ETH; 0x2: IP
|
||||||
|
} QMICTL_SET_DATA_FORMAT_TLV_LINK_PROT, *PQMICTL_SET_DATA_FORMAT_TLV_LINK_PROT;
|
||||||
|
|
||||||
|
#ifdef QCMP_UL_TLP
|
||||||
|
#define SET_DATA_FORMAT_TLV_TYPE_UL_TLP 0x11
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_UL_TLP {
|
||||||
|
UCHAR TLVType; // 0x11, Uplink TLP Setting
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR UlTlpSetting; // 0x0: Disable; 0x01: Enable
|
||||||
|
} QMICTL_SET_DATA_FORMAT_TLV_UL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_UL_TLP;
|
||||||
|
#endif // QCMP_UL_TLP
|
||||||
|
|
||||||
|
#ifdef QCMP_DL_TLP
|
||||||
|
#define SET_DATA_FORMAT_TLV_TYPE_DL_TLP 0x13
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_DL_TLP {
|
||||||
|
UCHAR TLVType; // 0x11, Uplink TLP Setting
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR DlTlpSetting; // 0x0: Disable; 0x01: Enable
|
||||||
|
} QMICTL_SET_DATA_FORMAT_TLV_DL_TLP, *PQMICTL_SET_DATA_FORMAT_TLV_DL_TLP;
|
||||||
|
#endif // QCMP_DL_TLP
|
||||||
|
|
||||||
|
#endif // QC_IP_MODE
|
||||||
|
|
||||||
|
#ifdef MP_QCQOS_ENABLED
|
||||||
|
#define SET_DATA_FORMAT_TLV_TYPE_QOS_SETTING 0x12
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING {
|
||||||
|
UCHAR TLVType; // 0x12, QoS setting
|
||||||
|
USHORT TLVLength; // 1
|
||||||
|
UCHAR QosSetting; // 0x0: Disable; 0x01: Enable
|
||||||
|
} QMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING,
|
||||||
|
*PQMICTL_SET_DATA_FORMAT_TLV_QOS_SETTING;
|
||||||
|
#endif // MP_QCQOS_ENABLED
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SET_DATA_FORMAT_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_SET_DATA_FORMAT_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult; // result code
|
||||||
|
USHORT QMIError; // error code
|
||||||
|
} __attribute__((packed)) QMICTL_SET_DATA_FORMAT_RESP_MSG,
|
||||||
|
*PQMICTL_SET_DATA_FORMAT_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SYNC_REQ_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_REQUEST
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_CTL_SYNC_REQ
|
||||||
|
USHORT Length; // 0
|
||||||
|
} __attribute__((packed)) QMICTL_SYNC_REQ_MSG, *PQMICTL_SYNC_REQ_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SYNC_RESP_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_RESPONSE
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_CTL_SYNC_RESP
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // QCTLV_TYPE_RESULT_CODE
|
||||||
|
USHORT TLVLength; // 0x0004
|
||||||
|
USHORT QMIResult;
|
||||||
|
USHORT QMIError;
|
||||||
|
} __attribute__((packed)) QMICTL_SYNC_RESP_MSG, *PQMICTL_SYNC_RESP_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_SYNC_IND_MSG {
|
||||||
|
UCHAR CtlFlags; // QMICTL_FLAG_INDICATION
|
||||||
|
UCHAR TransactionId;
|
||||||
|
USHORT QMICTLType; // QMICTL_REVOKE_CLIENT_ID_IND
|
||||||
|
USHORT Length;
|
||||||
|
} __attribute__((packed)) QMICTL_SYNC_IND_MSG, *PQMICTL_SYNC_IND_MSG;
|
||||||
|
|
||||||
|
typedef struct _QMICTL_MSG {
|
||||||
|
union {
|
||||||
|
// Message Header
|
||||||
|
QCQMICTL_MSG_HDR QMICTLMsgHdr;
|
||||||
|
QCQMICTL_MSG_HDR_RESP QMICTLMsgHdrRsp;
|
||||||
|
|
||||||
|
// QMICTL Message
|
||||||
|
QMICTL_SET_INSTANCE_ID_REQ_MSG SetInstanceIdReq;
|
||||||
|
QMICTL_SET_INSTANCE_ID_RESP_MSG SetInstanceIdRsp;
|
||||||
|
QMICTL_GET_VERSION_REQ_MSG GetVersionReq;
|
||||||
|
QMICTL_GET_VERSION_RESP_MSG GetVersionRsp;
|
||||||
|
QMICTL_GET_CLIENT_ID_REQ_MSG GetClientIdReq;
|
||||||
|
QMICTL_GET_CLIENT_ID_RESP_MSG GetClientIdRsp;
|
||||||
|
QMICTL_RELEASE_CLIENT_ID_REQ_MSG ReleaseClientIdReq;
|
||||||
|
QMICTL_RELEASE_CLIENT_ID_RESP_MSG ReleaseClientIdRsp;
|
||||||
|
QMICTL_REVOKE_CLIENT_ID_IND_MSG RevokeClientIdInd;
|
||||||
|
QMICTL_INVALID_CLIENT_ID_IND_MSG InvalidClientIdInd;
|
||||||
|
QMICTL_SET_DATA_FORMAT_REQ_MSG SetDataFormatReq;
|
||||||
|
QMICTL_SET_DATA_FORMAT_RESP_MSG SetDataFormatRsp;
|
||||||
|
QMICTL_SYNC_REQ_MSG SyncReq;
|
||||||
|
QMICTL_SYNC_RESP_MSG SyncRsp;
|
||||||
|
QMICTL_SYNC_IND_MSG SyncInd;
|
||||||
|
};
|
||||||
|
} __attribute__((packed)) QMICTL_MSG, *PQMICTL_MSG;
|
||||||
|
|
||||||
|
#endif // MPQCTL_H
|
287
package/wwan/app/fibocom-dial/src/MPQMI.h
Executable file
287
package/wwan/app/fibocom-dial/src/MPQMI.h
Executable file
@ -0,0 +1,287 @@
|
|||||||
|
/*===========================================================================
|
||||||
|
|
||||||
|
M P Q M I. H
|
||||||
|
DESCRIPTION:
|
||||||
|
|
||||||
|
This module contains forward references to the QMI module.
|
||||||
|
|
||||||
|
INITIALIZATION AND SEQUENCING REQUIREMENTS:
|
||||||
|
|
||||||
|
Copyright (C) 2011 by Qualcomm Technologies, Incorporated. All Rights Reserved.
|
||||||
|
===========================================================================*/
|
||||||
|
/*===========================================================================
|
||||||
|
|
||||||
|
EDIT HISTORY FOR FILE
|
||||||
|
$Header: //depot/QMI/win/qcdrivers/ndis/MPQMI.h#3 $
|
||||||
|
|
||||||
|
when who what, where, why
|
||||||
|
-------- --- ----------------------------------------------------------
|
||||||
|
11/20/04 hg Initial version.
|
||||||
|
===========================================================================*/
|
||||||
|
|
||||||
|
#ifndef USBQMI_H
|
||||||
|
#define USBQMI_H
|
||||||
|
|
||||||
|
typedef char CHAR;
|
||||||
|
typedef unsigned char UCHAR;
|
||||||
|
typedef unsigned short USHORT;
|
||||||
|
typedef int INT;
|
||||||
|
typedef unsigned int UINT;
|
||||||
|
typedef long LONG;
|
||||||
|
typedef unsigned int ULONG;
|
||||||
|
typedef unsigned long long ULONG64;
|
||||||
|
typedef char *PCHAR;
|
||||||
|
typedef unsigned char *PUCHAR;
|
||||||
|
typedef int *PINT;
|
||||||
|
typedef int BOOL;
|
||||||
|
|
||||||
|
#define TRUE (1 == 1)
|
||||||
|
#define FALSE (1 != 1)
|
||||||
|
|
||||||
|
#define QMICTL_SUPPORTED_MAJOR_VERSION 1
|
||||||
|
#define QMICTL_SUPPORTED_MINOR_VERSION 0
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
// ========= USB Control Message ==========
|
||||||
|
|
||||||
|
#define USB_CTL_MSG_TYPE_QMI 0x01
|
||||||
|
|
||||||
|
// USB Control Message
|
||||||
|
typedef struct _QCUSB_CTL_MSG_HDR {
|
||||||
|
UCHAR IFType;
|
||||||
|
} __attribute__((packed)) QCUSB_CTL_MSG_HDR, *PQCUSB_CTL_MSG_HDR;
|
||||||
|
|
||||||
|
#define QCUSB_CTL_MSG_HDR_SIZE sizeof(QCUSB_CTL_MSG_HDR)
|
||||||
|
|
||||||
|
typedef struct _QCUSB_CTL_MSG {
|
||||||
|
UCHAR IFType;
|
||||||
|
UCHAR Message;
|
||||||
|
} __attribute__((packed)) QCUSB_CTL_MSG, *PQCUSB_CTL_MSG;
|
||||||
|
|
||||||
|
#define QCTLV_TYPE_REQUIRED_PARAMETER 0x01
|
||||||
|
#define QCTLV_TYPE_RESULT_CODE 0x02
|
||||||
|
|
||||||
|
// ================= QMI ==================
|
||||||
|
|
||||||
|
// Define QMI Type
|
||||||
|
typedef enum _QMI_SERVICE_TYPE {
|
||||||
|
QMUX_TYPE_CTL = 0x00,
|
||||||
|
QMUX_TYPE_WDS = 0x01,
|
||||||
|
QMUX_TYPE_DMS = 0x02,
|
||||||
|
QMUX_TYPE_NAS = 0x03,
|
||||||
|
QMUX_TYPE_QOS = 0x04,
|
||||||
|
QMUX_TYPE_WMS = 0x05,
|
||||||
|
QMUX_TYPE_PDS = 0x06,
|
||||||
|
QMUX_TYPE_UIM = 0x0B,
|
||||||
|
QMUX_TYPE_WDS_IPV6 = 0x11,
|
||||||
|
QMUX_TYPE_WDS_ADMIN = 0x1A,
|
||||||
|
QMUX_TYPE_MAX = 0xFF,
|
||||||
|
QMUX_TYPE_ALL = 0xFF
|
||||||
|
} QMI_SERVICE_TYPE;
|
||||||
|
|
||||||
|
typedef enum _QMI_RESULT_CODE_TYPE {
|
||||||
|
QMI_RESULT_SUCCESS = 0x0000,
|
||||||
|
QMI_RESULT_FAILURE = 0x0001
|
||||||
|
} QMI_RESULT_CODE_TYPE;
|
||||||
|
|
||||||
|
typedef enum _QMI_ERROR_CODE_TYPE {
|
||||||
|
QMI_ERR_NONE = 0x0000,
|
||||||
|
QMI_ERR_MALFORMED_MSG = 0x0001,
|
||||||
|
QMI_ERR_NO_MEMORY = 0x0002,
|
||||||
|
QMI_ERR_INTERNAL = 0x0003,
|
||||||
|
QMI_ERR_ABORTED = 0x0004,
|
||||||
|
QMI_ERR_CLIENT_IDS_EXHAUSTED = 0x0005,
|
||||||
|
QMI_ERR_UNABORTABLE_TRANSACTION = 0x0006,
|
||||||
|
QMI_ERR_INVALID_CLIENT_ID = 0x0007,
|
||||||
|
QMI_ERR_NO_THRESHOLDS = 0x0008,
|
||||||
|
QMI_ERR_INVALID_HANDLE = 0x0009,
|
||||||
|
QMI_ERR_INVALID_PROFILE = 0x000A,
|
||||||
|
QMI_ERR_INVALID_PINID = 0x000B,
|
||||||
|
QMI_ERR_INCORRECT_PIN = 0x000C,
|
||||||
|
QMI_ERR_NO_NETWORK_FOUND = 0x000D,
|
||||||
|
QMI_ERR_CALL_FAILED = 0x000E,
|
||||||
|
QMI_ERR_OUT_OF_CALL = 0x000F,
|
||||||
|
QMI_ERR_NOT_PROVISIONED = 0x0010,
|
||||||
|
QMI_ERR_MISSING_ARG = 0x0011,
|
||||||
|
QMI_ERR_ARG_TOO_LONG = 0x0013,
|
||||||
|
QMI_ERR_INVALID_TX_ID = 0x0016,
|
||||||
|
QMI_ERR_DEVICE_IN_USE = 0x0017,
|
||||||
|
QMI_ERR_OP_NETWORK_UNSUPPORTED = 0x0018,
|
||||||
|
QMI_ERR_OP_DEVICE_UNSUPPORTED = 0x0019,
|
||||||
|
QMI_ERR_NO_EFFECT = 0x001A,
|
||||||
|
QMI_ERR_NO_FREE_PROFILE = 0x001B,
|
||||||
|
QMI_ERR_INVALID_PDP_TYPE = 0x001C,
|
||||||
|
QMI_ERR_INVALID_TECH_PREF = 0x001D,
|
||||||
|
QMI_ERR_INVALID_PROFILE_TYPE = 0x001E,
|
||||||
|
QMI_ERR_INVALID_SERVICE_TYPE = 0x001F,
|
||||||
|
QMI_ERR_INVALID_REGISTER_ACTION = 0x0020,
|
||||||
|
QMI_ERR_INVALID_PS_ATTACH_ACTION = 0x0021,
|
||||||
|
QMI_ERR_AUTHENTICATION_FAILED = 0x0022,
|
||||||
|
QMI_ERR_PIN_BLOCKED = 0x0023,
|
||||||
|
QMI_ERR_PIN_PERM_BLOCKED = 0x0024,
|
||||||
|
QMI_ERR_SIM_NOT_INITIALIZED = 0x0025,
|
||||||
|
QMI_ERR_MAX_QOS_REQUESTS_IN_USE = 0x0026,
|
||||||
|
QMI_ERR_INCORRECT_FLOW_FILTER = 0x0027,
|
||||||
|
QMI_ERR_NETWORK_QOS_UNAWARE = 0x0028,
|
||||||
|
QMI_ERR_INVALID_QOS_ID = 0x0029,
|
||||||
|
QMI_ERR_INVALID_ID = 0x0029,
|
||||||
|
QMI_ERR_REQUESTED_NUM_UNSUPPORTED = 0x002A,
|
||||||
|
QMI_ERR_INTERFACE_NOT_FOUND = 0x002B,
|
||||||
|
QMI_ERR_FLOW_SUSPENDED = 0x002C,
|
||||||
|
QMI_ERR_INVALID_DATA_FORMAT = 0x002D,
|
||||||
|
QMI_ERR_GENERAL = 0x002E,
|
||||||
|
QMI_ERR_UNKNOWN = 0x002F,
|
||||||
|
QMI_ERR_INVALID_ARG = 0x0030,
|
||||||
|
QMI_ERR_INVALID_INDEX = 0x0031,
|
||||||
|
QMI_ERR_NO_ENTRY = 0x0032,
|
||||||
|
QMI_ERR_DEVICE_STORAGE_FULL = 0x0033,
|
||||||
|
QMI_ERR_DEVICE_NOT_READY = 0x0034,
|
||||||
|
QMI_ERR_NETWORK_NOT_READY = 0x0035,
|
||||||
|
QMI_ERR_CAUSE_CODE = 0x0036,
|
||||||
|
QMI_ERR_MESSAGE_NOT_SENT = 0x0037,
|
||||||
|
QMI_ERR_MESSAGE_DELIVERY_FAILURE = 0x0038,
|
||||||
|
QMI_ERR_INVALID_MESSAGE_ID = 0x0039,
|
||||||
|
QMI_ERR_ENCODING = 0x003A,
|
||||||
|
QMI_ERR_AUTHENTICATION_LOCK = 0x003B,
|
||||||
|
QMI_ERR_INVALID_TRANSITION = 0x003C,
|
||||||
|
QMI_ERR_NOT_A_MCAST_IFACE = 0x003D,
|
||||||
|
QMI_ERR_MAX_MCAST_REQUESTS_IN_USE = 0x003E,
|
||||||
|
QMI_ERR_INVALID_MCAST_HANDLE = 0x003F,
|
||||||
|
QMI_ERR_INVALID_IP_FAMILY_PREF = 0x0040,
|
||||||
|
QMI_ERR_SESSION_INACTIVE = 0x0041,
|
||||||
|
QMI_ERR_SESSION_INVALID = 0x0042,
|
||||||
|
QMI_ERR_SESSION_OWNERSHIP = 0x0043,
|
||||||
|
QMI_ERR_INSUFFICIENT_RESOURCES = 0x0044,
|
||||||
|
QMI_ERR_DISABLED = 0x0045,
|
||||||
|
QMI_ERR_INVALID_OPERATION = 0x0046,
|
||||||
|
QMI_ERR_INVALID_QMI_CMD = 0x0047,
|
||||||
|
QMI_ERR_TPDU_TYPE = 0x0048,
|
||||||
|
QMI_ERR_SMSC_ADDR = 0x0049,
|
||||||
|
QMI_ERR_INFO_UNAVAILABLE = 0x004A,
|
||||||
|
QMI_ERR_SEGMENT_TOO_LONG = 0x004B,
|
||||||
|
QMI_ERR_SEGMENT_ORDER = 0x004C,
|
||||||
|
QMI_ERR_BUNDLING_NOT_SUPPORTED = 0x004D,
|
||||||
|
QMI_ERR_OP_PARTIAL_FAILURE = 0x004E,
|
||||||
|
QMI_ERR_POLICY_MISMATCH = 0x004F,
|
||||||
|
QMI_ERR_SIM_FILE_NOT_FOUND = 0x0050,
|
||||||
|
QMI_ERR_EXTENDED_INTERNAL = 0x0051,
|
||||||
|
QMI_ERR_ACCESS_DENIED = 0x0052,
|
||||||
|
QMI_ERR_HARDWARE_RESTRICTED = 0x0053,
|
||||||
|
QMI_ERR_ACK_NOT_SENT = 0x0054,
|
||||||
|
QMI_ERR_INJECT_TIMEOUT = 0x0055,
|
||||||
|
QMI_ERR_INCOMPATIBLE_STATE = 0x005A,
|
||||||
|
QMI_ERR_FDN_RESTRICT = 0x005B,
|
||||||
|
QMI_ERR_SUPS_FAILURE_CAUSE = 0x005C,
|
||||||
|
QMI_ERR_NO_RADIO = 0x005D,
|
||||||
|
QMI_ERR_NOT_SUPPORTED = 0x005E,
|
||||||
|
QMI_ERR_NO_SUBSCRIPTION = 0x005F,
|
||||||
|
QMI_ERR_CARD_CALL_CONTROL_FAILED = 0x0060,
|
||||||
|
QMI_ERR_NETWORK_ABORTED = 0x0061,
|
||||||
|
QMI_ERR_MSG_BLOCKED = 0x0062,
|
||||||
|
QMI_ERR_INVALID_SESSION_TYPE = 0x0064,
|
||||||
|
QMI_ERR_INVALID_PB_TYPE = 0x0065,
|
||||||
|
QMI_ERR_NO_SIM = 0x0066,
|
||||||
|
QMI_ERR_PB_NOT_READY = 0x0067,
|
||||||
|
QMI_ERR_PIN_RESTRICTION = 0x0068,
|
||||||
|
QMI_ERR_PIN2_RESTRICTION = 0x0069,
|
||||||
|
QMI_ERR_PUK_RESTRICTION = 0x006A,
|
||||||
|
QMI_ERR_PUK2_RESTRICTION = 0x006B,
|
||||||
|
QMI_ERR_PB_ACCESS_RESTRICTED = 0x006C,
|
||||||
|
QMI_ERR_PB_DELETE_IN_PROG = 0x006D,
|
||||||
|
QMI_ERR_PB_TEXT_TOO_LONG = 0x006E,
|
||||||
|
QMI_ERR_PB_NUMBER_TOO_LONG = 0x006F,
|
||||||
|
QMI_ERR_PB_HIDDEN_KEY_RESTRICTION = 0x0070
|
||||||
|
} QMI_ERROR_CODE_TYPE;
|
||||||
|
|
||||||
|
#define QCQMI_CTL_FLAG_SERVICE 0x80
|
||||||
|
#define QCQMI_CTL_FLAG_CTL_POINT 0x00
|
||||||
|
|
||||||
|
typedef struct _QCQMI_HDR {
|
||||||
|
UCHAR IFType;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR CtlFlags; // reserved
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
} __attribute__((packed)) QCQMI_HDR, *PQCQMI_HDR;
|
||||||
|
|
||||||
|
#define QCQMI_HDR_SIZE (sizeof(QCQMI_HDR) - 1)
|
||||||
|
|
||||||
|
typedef struct _QCQMI {
|
||||||
|
UCHAR IFType;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR CtlFlags; // reserved
|
||||||
|
UCHAR QMIType;
|
||||||
|
UCHAR ClientId;
|
||||||
|
UCHAR SDU;
|
||||||
|
} __attribute__((packed)) QCQMI, *PQCQMI;
|
||||||
|
|
||||||
|
typedef struct _QMI_SERVICE_VERSION {
|
||||||
|
USHORT Major;
|
||||||
|
USHORT Minor;
|
||||||
|
USHORT AddendumMajor;
|
||||||
|
USHORT AddendumMinor;
|
||||||
|
} __attribute__((packed)) QMI_SERVICE_VERSION, *PQMI_SERVICE_VERSION;
|
||||||
|
|
||||||
|
// ================= QMUX ==================
|
||||||
|
|
||||||
|
#define QMUX_MSG_OVERHEAD_BYTES 4 // Type(USHORT) Length(USHORT) -- header
|
||||||
|
|
||||||
|
#define QMUX_BROADCAST_CID 0xFF
|
||||||
|
|
||||||
|
typedef struct _QCQMUX_HDR {
|
||||||
|
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
|
||||||
|
USHORT TransactionId;
|
||||||
|
} __attribute__((packed)) QCQMUX_HDR, *PQCQMUX_HDR;
|
||||||
|
|
||||||
|
typedef struct _QCQMUX {
|
||||||
|
UCHAR CtlFlags; // 0: single QMUX Msg; 1:
|
||||||
|
USHORT TransactionId;
|
||||||
|
UCHAR Message; // Type(2), Length(2), Value
|
||||||
|
} __attribute__((packed)) QCQMUX, *PQCQMUX;
|
||||||
|
|
||||||
|
#define QCQMUX_HDR_SIZE sizeof(QCQMUX_HDR)
|
||||||
|
|
||||||
|
typedef struct _QCQMUX_MSG_HDR {
|
||||||
|
USHORT Type;
|
||||||
|
USHORT Length;
|
||||||
|
} __attribute__((packed)) QCQMUX_MSG_HDR, *PQCQMUX_MSG_HDR;
|
||||||
|
|
||||||
|
#define QCQMUX_MSG_HDR_SIZE sizeof(QCQMUX_MSG_HDR)
|
||||||
|
|
||||||
|
typedef struct _QCQMUX_MSG_HDR_RESP {
|
||||||
|
USHORT Type;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR TLVType; // 0x02 - result code
|
||||||
|
USHORT TLVLength; // 4
|
||||||
|
USHORT QMUXResult; // QMI_RESULT_SUCCESS
|
||||||
|
// QMI_RESULT_FAILURE
|
||||||
|
USHORT QMUXError; // QMI_ERR_INVALID_ARG
|
||||||
|
// QMI_ERR_NO_MEMORY
|
||||||
|
// QMI_ERR_INTERNAL
|
||||||
|
// QMI_ERR_FAULT
|
||||||
|
} __attribute__((packed)) QCQMUX_MSG_HDR_RESP, *PQCQMUX_MSG_HDR_RESP;
|
||||||
|
|
||||||
|
typedef struct _QCQMUX_TLV {
|
||||||
|
UCHAR Type;
|
||||||
|
USHORT Length;
|
||||||
|
UCHAR Value;
|
||||||
|
} __attribute__((packed)) QCQMUX_TLV, *PQCQMUX_TLV;
|
||||||
|
|
||||||
|
typedef struct _QMI_TLV_HDR {
|
||||||
|
UCHAR TLVType;
|
||||||
|
USHORT TLVLength;
|
||||||
|
} __attribute__((packed)) QMI_TLV_HDR, *PQMI_TLV_HDR;
|
||||||
|
|
||||||
|
// QMUX Message Definitions -- QMI SDU
|
||||||
|
#define QMUX_CTL_FLAG_SINGLE_MSG 0x00
|
||||||
|
#define QMUX_CTL_FLAG_COMPOUND_MSG 0x01
|
||||||
|
#define QMUX_CTL_FLAG_TYPE_CMD 0x00
|
||||||
|
#define QMUX_CTL_FLAG_TYPE_RSP 0x02
|
||||||
|
#define QMUX_CTL_FLAG_TYPE_IND 0x04
|
||||||
|
#define QMUX_CTL_FLAG_MASK_COMPOUND 0x01
|
||||||
|
#define QMUX_CTL_FLAG_MASK_TYPE 0x06 // 00-cmd, 01-rsp, 10-ind
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#endif // USBQMI_H
|
437
package/wwan/app/fibocom-dial/src/MPQMUX.c
Executable file
437
package/wwan/app/fibocom-dial/src/MPQMUX.c
Executable file
@ -0,0 +1,437 @@
|
|||||||
|
#include "QMIThread.h"
|
||||||
|
static char line[1024];
|
||||||
|
static pthread_mutex_t dumpQMIMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#undef dbg
|
||||||
|
#define dbg(format, arg...) \
|
||||||
|
do { \
|
||||||
|
if (strlen(line) < sizeof(line)) \
|
||||||
|
snprintf(&line[strlen(line)], sizeof(line) - strlen(line), format, \
|
||||||
|
##arg); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
PQMI_TLV_HDR GetTLV(PQCQMUX_MSG_HDR pQMUXMsgHdr, int TLVType);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
UINT type;
|
||||||
|
const char *name;
|
||||||
|
} QMI_NAME_T;
|
||||||
|
|
||||||
|
#define qmi_name_item(type) \
|
||||||
|
{ \
|
||||||
|
type, #type \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_ctl_QMICTLType[] = {
|
||||||
|
// QMICTL Type
|
||||||
|
qmi_name_item(QMICTL_SET_INSTANCE_ID_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMICTL_SET_INSTANCE_ID_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMICTL_GET_VERSION_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMICTL_GET_VERSION_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMICTL_GET_CLIENT_ID_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMICTL_GET_CLIENT_ID_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMICTL_RELEASE_CLIENT_ID_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMICTL_REVOKE_CLIENT_ID_IND), // 0x0024
|
||||||
|
qmi_name_item(QMICTL_INVALID_CLIENT_ID_IND), // 0x0025
|
||||||
|
qmi_name_item(QMICTL_SET_DATA_FORMAT_REQ), // 0x0026
|
||||||
|
qmi_name_item(QMICTL_SET_DATA_FORMAT_RESP), // 0x0026
|
||||||
|
qmi_name_item(QMICTL_SYNC_REQ), // 0x0027
|
||||||
|
qmi_name_item(QMICTL_SYNC_RESP), // 0x0027
|
||||||
|
qmi_name_item(QMICTL_SYNC_IND), // 0x0027
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_CtlFlags[] = {
|
||||||
|
qmi_name_item(QMUX_CTL_FLAG_TYPE_CMD),
|
||||||
|
qmi_name_item(QMUX_CTL_FLAG_TYPE_RSP),
|
||||||
|
qmi_name_item(QMUX_CTL_FLAG_TYPE_IND),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_wds_Type[] = {
|
||||||
|
qmi_name_item(QMIWDS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||||
|
qmi_name_item(QMIWDS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||||
|
qmi_name_item(QMIWDS_EVENT_REPORT_IND), // 0x0001
|
||||||
|
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMIWDS_START_NETWORK_INTERFACE_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMIWDS_STOP_NETWORK_INTERFACE_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMIWDS_GET_PKT_SRVC_STATUS_IND), // 0x0022
|
||||||
|
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMIWDS_GET_CURRENT_CHANNEL_RATE_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_REQ), // 0x0024
|
||||||
|
qmi_name_item(QMIWDS_GET_PKT_STATISTICS_RESP), // 0x0024
|
||||||
|
//begin modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610
|
||||||
|
qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_REQ), // 0x0027
|
||||||
|
qmi_name_item(QMIWDS_CREATE_PROFILE_SETTINGS_RESP), // 0x0027
|
||||||
|
//end modified by zhangkaibo add create profile qmi. mantis 0049137,0048741 20200610
|
||||||
|
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_REQ), // 0x0028
|
||||||
|
qmi_name_item(QMIWDS_MODIFY_PROFILE_SETTINGS_RESP), // 0x0028
|
||||||
|
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_REQ), // 0x002B
|
||||||
|
qmi_name_item(QMIWDS_GET_PROFILE_SETTINGS_RESP), // 0x002BD
|
||||||
|
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_REQ), // 0x002C
|
||||||
|
qmi_name_item(QMIWDS_GET_DEFAULT_SETTINGS_RESP), // 0x002C
|
||||||
|
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_REQ), // 0x002D
|
||||||
|
qmi_name_item(QMIWDS_GET_RUNTIME_SETTINGS_RESP), // 0x002D
|
||||||
|
qmi_name_item(QMIWDS_GET_MIP_MODE_REQ), // 0x002F
|
||||||
|
qmi_name_item(QMIWDS_GET_MIP_MODE_RESP), // 0x002F
|
||||||
|
qmi_name_item(QMIWDS_GET_DATA_BEARER_REQ), // 0x0037
|
||||||
|
qmi_name_item(QMIWDS_GET_DATA_BEARER_RESP), // 0x0037
|
||||||
|
qmi_name_item(QMIWDS_DUN_CALL_INFO_REQ), // 0x0038
|
||||||
|
qmi_name_item(QMIWDS_DUN_CALL_INFO_RESP), // 0x0038
|
||||||
|
qmi_name_item(QMIWDS_DUN_CALL_INFO_IND), // 0x0038
|
||||||
|
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_REQ), // 0x004D
|
||||||
|
qmi_name_item(QMIWDS_SET_CLIENT_IP_FAMILY_PREF_RESP), // 0x004D
|
||||||
|
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_REQ), // 0x0051
|
||||||
|
qmi_name_item(QMIWDS_SET_AUTO_CONNECT_RESP), // 0x0051
|
||||||
|
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_REQ), // 0x00A2
|
||||||
|
qmi_name_item(QMIWDS_BIND_MUX_DATA_PORT_RESP), // 0x00A2
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_dms_Type[] = {
|
||||||
|
// ======================= DMS ==============================
|
||||||
|
qmi_name_item(QMIDMS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||||
|
qmi_name_item(QMIDMS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||||
|
qmi_name_item(QMIDMS_EVENT_REPORT_IND), // 0x0001
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_CAP_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_CAP_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_MFR_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_MFR_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_MODEL_ID_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_REV_ID_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMIDMS_GET_MSISDN_REQ), // 0x0024
|
||||||
|
qmi_name_item(QMIDMS_GET_MSISDN_RESP), // 0x0024
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_REQ), // 0x0025
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_SERIAL_NUMBERS_RESP), // 0x0025
|
||||||
|
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_REQ), // 0x0027
|
||||||
|
qmi_name_item(QMIDMS_UIM_SET_PIN_PROTECTION_RESP), // 0x0027
|
||||||
|
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_REQ), // 0x0028
|
||||||
|
qmi_name_item(QMIDMS_UIM_VERIFY_PIN_RESP), // 0x0028
|
||||||
|
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_REQ), // 0x0029
|
||||||
|
qmi_name_item(QMIDMS_UIM_UNBLOCK_PIN_RESP), // 0x0029
|
||||||
|
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_REQ), // 0x002A
|
||||||
|
qmi_name_item(QMIDMS_UIM_CHANGE_PIN_RESP), // 0x002A
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_REQ), // 0x002B
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_PIN_STATUS_RESP), // 0x002B
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_REQ), // 0x002C
|
||||||
|
qmi_name_item(QMIDMS_GET_DEVICE_HARDWARE_REV_RESP), // 0x002C
|
||||||
|
qmi_name_item(QMIDMS_GET_OPERATING_MODE_REQ), // 0x002D
|
||||||
|
qmi_name_item(QMIDMS_GET_OPERATING_MODE_RESP), // 0x002D
|
||||||
|
qmi_name_item(QMIDMS_SET_OPERATING_MODE_REQ), // 0x002E
|
||||||
|
qmi_name_item(QMIDMS_SET_OPERATING_MODE_RESP), // 0x002E
|
||||||
|
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_REQ), // 0x0031
|
||||||
|
qmi_name_item(QMIDMS_GET_ACTIVATED_STATUS_RESP), // 0x0031
|
||||||
|
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_REQ), // 0x0032
|
||||||
|
qmi_name_item(QMIDMS_ACTIVATE_AUTOMATIC_RESP), // 0x0032
|
||||||
|
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_REQ), // 0x0033
|
||||||
|
qmi_name_item(QMIDMS_ACTIVATE_MANUAL_RESP), // 0x0033
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_ICCID_REQ), // 0x003C
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_ICCID_RESP), // 0x003C
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_REQ), // 0x0040
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_CK_STATUS_RESP), // 0x0040
|
||||||
|
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_REQ), // 0x0041
|
||||||
|
qmi_name_item(QMIDMS_UIM_SET_CK_PROTECTION_RESP), // 0x0041
|
||||||
|
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_REQ), // 0x0042
|
||||||
|
qmi_name_item(QMIDMS_UIM_UNBLOCK_CK_RESP), // 0x0042
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_IMSI_REQ), // 0x0043
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_IMSI_RESP), // 0x0043
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_STATE_REQ), // 0x0044
|
||||||
|
qmi_name_item(QMIDMS_UIM_GET_STATE_RESP), // 0x0044
|
||||||
|
qmi_name_item(QMIDMS_GET_BAND_CAP_REQ), // 0x0045
|
||||||
|
qmi_name_item(QMIDMS_GET_BAND_CAP_RESP), // 0x0045
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_nas_Type[] = {
|
||||||
|
// ======================= NAS ==============================
|
||||||
|
qmi_name_item(QMINAS_SET_EVENT_REPORT_REQ), // 0x0002
|
||||||
|
qmi_name_item(QMINAS_SET_EVENT_REPORT_RESP), // 0x0002
|
||||||
|
qmi_name_item(QMINAS_EVENT_REPORT_IND), // 0x0002
|
||||||
|
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMINAS_GET_SIGNAL_STRENGTH_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMINAS_PERFORM_NETWORK_SCAN_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMINAS_INITIATE_NW_REGISTER_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMINAS_INITIATE_ATTACH_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMINAS_INITIATE_ATTACH_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_REQ), // 0x0024
|
||||||
|
qmi_name_item(QMINAS_GET_SERVING_SYSTEM_RESP), // 0x0024
|
||||||
|
qmi_name_item(QMINAS_SERVING_SYSTEM_IND), // 0x0024
|
||||||
|
qmi_name_item(QMINAS_GET_HOME_NETWORK_REQ), // 0x0025
|
||||||
|
qmi_name_item(QMINAS_GET_HOME_NETWORK_RESP), // 0x0025
|
||||||
|
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_REQ), // 0x0026
|
||||||
|
qmi_name_item(QMINAS_GET_PREFERRED_NETWORK_RESP), // 0x0026
|
||||||
|
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_REQ), // 0x0027
|
||||||
|
qmi_name_item(QMINAS_SET_PREFERRED_NETWORK_RESP), // 0x0027
|
||||||
|
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_REQ), // 0x0028
|
||||||
|
qmi_name_item(QMINAS_GET_FORBIDDEN_NETWORK_RESP), // 0x0028
|
||||||
|
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_REQ), // 0x0029
|
||||||
|
qmi_name_item(QMINAS_SET_FORBIDDEN_NETWORK_RESP), // 0x0029
|
||||||
|
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_REQ), // 0x002A
|
||||||
|
qmi_name_item(QMINAS_SET_TECHNOLOGY_PREF_RESP), // 0x002A
|
||||||
|
qmi_name_item(QMINAS_GET_RF_BAND_INFO_REQ), // 0x0031
|
||||||
|
qmi_name_item(QMINAS_GET_RF_BAND_INFO_RESP), // 0x0031
|
||||||
|
qmi_name_item(QMINAS_GET_PLMN_NAME_REQ), // 0x0044
|
||||||
|
qmi_name_item(QMINAS_GET_PLMN_NAME_RESP), // 0x0044
|
||||||
|
qmi_name_item(FIBO_PACKET_TRANSFER_START_IND), // 0X100
|
||||||
|
qmi_name_item(FIBO_PACKET_TRANSFER_END_IND), // 0X101
|
||||||
|
qmi_name_item(QMINAS_GET_SYS_INFO_REQ), // 0x004D
|
||||||
|
qmi_name_item(QMINAS_GET_SYS_INFO_RESP), // 0x004D
|
||||||
|
qmi_name_item(QMINAS_SYS_INFO_IND), // 0x004D
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_wms_Type[] = {
|
||||||
|
// ======================= WMS ==============================
|
||||||
|
qmi_name_item(QMIWMS_SET_EVENT_REPORT_REQ), // 0x0001
|
||||||
|
qmi_name_item(QMIWMS_SET_EVENT_REPORT_RESP), // 0x0001
|
||||||
|
qmi_name_item(QMIWMS_EVENT_REPORT_IND), // 0x0001
|
||||||
|
qmi_name_item(QMIWMS_RAW_SEND_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMIWMS_RAW_SEND_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMIWMS_RAW_WRITE_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMIWMS_RAW_WRITE_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMIWMS_RAW_READ_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMIWMS_RAW_READ_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMIWMS_MODIFY_TAG_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMIWMS_MODIFY_TAG_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMIWMS_DELETE_REQ), // 0x0024
|
||||||
|
qmi_name_item(QMIWMS_DELETE_RESP), // 0x0024
|
||||||
|
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_REQ), // 0x0030
|
||||||
|
qmi_name_item(QMIWMS_GET_MESSAGE_PROTOCOL_RESP), // 0x0030
|
||||||
|
qmi_name_item(QMIWMS_LIST_MESSAGES_REQ), // 0x0031
|
||||||
|
qmi_name_item(QMIWMS_LIST_MESSAGES_RESP), // 0x0031
|
||||||
|
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_REQ), // 0x0034
|
||||||
|
qmi_name_item(QMIWMS_GET_SMSC_ADDRESS_RESP), // 0x0034
|
||||||
|
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_REQ), // 0x0035
|
||||||
|
qmi_name_item(QMIWMS_SET_SMSC_ADDRESS_RESP), // 0x0035
|
||||||
|
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_REQ), // 0x0036
|
||||||
|
qmi_name_item(QMIWMS_GET_STORE_MAX_SIZE_RESP), // 0x0036
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_wds_admin_Type[] = {
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_SET_DATA_FORMAT_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_GET_DATA_FORMAT_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_REQ), // 0x002B
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_SET_QMAP_SETTINGS_RESP), // 0x002B
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_REQ), // 0x002C
|
||||||
|
qmi_name_item(QMIWDS_ADMIN_GET_QMAP_SETTINGS_RESP), // 0x002C
|
||||||
|
};
|
||||||
|
|
||||||
|
static const QMI_NAME_T qmux_uim_Type[] = {
|
||||||
|
qmi_name_item(QMIUIM_READ_TRANSPARENT_REQ), // 0x0020
|
||||||
|
qmi_name_item(QMIUIM_READ_TRANSPARENT_RESP), // 0x0020
|
||||||
|
qmi_name_item(QMIUIM_READ_TRANSPARENT_IND), // 0x0020
|
||||||
|
qmi_name_item(QMIUIM_READ_RECORD_REQ), // 0x0021
|
||||||
|
qmi_name_item(QMIUIM_READ_RECORD_RESP), // 0x0021
|
||||||
|
qmi_name_item(QMIUIM_READ_RECORD_IND), // 0x0021
|
||||||
|
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_REQ), // 0x0022
|
||||||
|
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_RESP), // 0x0022
|
||||||
|
qmi_name_item(QMIUIM_WRITE_TRANSPARENT_IND), // 0x0022
|
||||||
|
qmi_name_item(QMIUIM_WRITE_RECORD_REQ), // 0x0023
|
||||||
|
qmi_name_item(QMIUIM_WRITE_RECORD_RESP), // 0x0023
|
||||||
|
qmi_name_item(QMIUIM_WRITE_RECORD_IND), // 0x0023
|
||||||
|
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_REQ), // 0x0025
|
||||||
|
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_RESP), // 0x0025
|
||||||
|
qmi_name_item(QMIUIM_SET_PIN_PROTECTION_IND), // 0x0025
|
||||||
|
qmi_name_item(QMIUIM_VERIFY_PIN_REQ), // 0x0026
|
||||||
|
qmi_name_item(QMIUIM_VERIFY_PIN_RESP), // 0x0026
|
||||||
|
qmi_name_item(QMIUIM_VERIFY_PIN_IND), // 0x0026
|
||||||
|
qmi_name_item(QMIUIM_UNBLOCK_PIN_REQ), // 0x0027
|
||||||
|
qmi_name_item(QMIUIM_UNBLOCK_PIN_RESP), // 0x0027
|
||||||
|
qmi_name_item(QMIUIM_UNBLOCK_PIN_IND), // 0x0027
|
||||||
|
qmi_name_item(QMIUIM_CHANGE_PIN_REQ), // 0x0028
|
||||||
|
qmi_name_item(QMIUIM_CHANGE_PIN_RESP), // 0x0028
|
||||||
|
qmi_name_item(QMIUIM_CHANGE_PIN_IND), // 0x0028
|
||||||
|
qmi_name_item(QMIUIM_DEPERSONALIZATION_REQ), // 0x0029
|
||||||
|
qmi_name_item(QMIUIM_DEPERSONALIZATION_RESP), // 0x0029
|
||||||
|
qmi_name_item(QMIUIM_EVENT_REG_REQ), // 0x002E
|
||||||
|
qmi_name_item(QMIUIM_EVENT_REG_RESP), // 0x002E
|
||||||
|
qmi_name_item(QMIUIM_GET_CARD_STATUS_REQ), // 0x002F
|
||||||
|
qmi_name_item(QMIUIM_GET_CARD_STATUS_RESP), // 0x002F
|
||||||
|
qmi_name_item(QMIUIM_STATUS_CHANGE_IND), // 0x0032
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *qmi_name_get(const QMI_NAME_T *table, size_t size, int type,
|
||||||
|
const char *tag)
|
||||||
|
{
|
||||||
|
static char unknow[40];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (qmux_CtlFlags == table) {
|
||||||
|
if (!strcmp(tag, "_REQ"))
|
||||||
|
tag = "_CMD";
|
||||||
|
else if (!strcmp(tag, "_RESP"))
|
||||||
|
tag = "_RSP";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
if (table[i].type == (UINT)type) {
|
||||||
|
if (!tag || (strstr(table[i].name, tag)))
|
||||||
|
return table[i].name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sprintf(unknow, "unknow_%x", type);
|
||||||
|
return unknow;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define QMI_NAME(table, type) \
|
||||||
|
qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, 0)
|
||||||
|
#define QMUX_NAME(table, type, tag) \
|
||||||
|
qmi_name_get(table, sizeof(table) / sizeof(table[0]), type, tag)
|
||||||
|
|
||||||
|
void dump_tlv(PQCQMUX_MSG_HDR pQMUXMsgHdr)
|
||||||
|
{
|
||||||
|
int TLVFind = 0;
|
||||||
|
int i;
|
||||||
|
// dbg("QCQMUX_TLV-----------------------------------\n");
|
||||||
|
// dbg("{Type,\tLength,\tValue}\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
PQMI_TLV_HDR TLVHdr = GetTLV(pQMUXMsgHdr, 0x1000 + (++TLVFind));
|
||||||
|
if (TLVHdr == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// if ((TLVHdr->TLVType == 0x02) && ((USHORT *)(TLVHdr+1))[0])
|
||||||
|
{
|
||||||
|
dbg("{%02x,\t%04x,\t", TLVHdr->TLVType,
|
||||||
|
le16_to_cpu(TLVHdr->TLVLength));
|
||||||
|
for (i = 0; i < le16_to_cpu(TLVHdr->TLVLength); i++) {
|
||||||
|
dbg("%02x ", ((UCHAR *)(TLVHdr + 1))[i]);
|
||||||
|
}
|
||||||
|
dbg("}\n");
|
||||||
|
}
|
||||||
|
} // while
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_ctl(PQCQMICTL_MSG_HDR CTLHdr)
|
||||||
|
{
|
||||||
|
const char *tag;
|
||||||
|
|
||||||
|
// dbg("QCQMICTL_MSG--------------------------------------------\n");
|
||||||
|
// dbg("CtlFlags: %02x\t\t%s\n", CTLHdr->CtlFlags,
|
||||||
|
// QMI_NAME(qmi_ctl_CtlFlags, CTLHdr->CtlFlags));
|
||||||
|
dbg("TransactionId: %02x\n", CTLHdr->TransactionId);
|
||||||
|
switch (CTLHdr->CtlFlags) {
|
||||||
|
case QMICTL_FLAG_REQUEST:
|
||||||
|
tag = "_REQ";
|
||||||
|
break;
|
||||||
|
case QMICTL_FLAG_RESPONSE:
|
||||||
|
tag = "_RESP";
|
||||||
|
break;
|
||||||
|
case QMICTL_FLAG_INDICATION:
|
||||||
|
tag = "_IND";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tag = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dbg("QMICTLType: %04x\t%s\n", le16_to_cpu(CTLHdr->QMICTLType),
|
||||||
|
QMUX_NAME(qmux_ctl_QMICTLType, le16_to_cpu(CTLHdr->QMICTLType), tag));
|
||||||
|
dbg("Length: %04x\n", le16_to_cpu(CTLHdr->Length));
|
||||||
|
|
||||||
|
dump_tlv((PQCQMUX_MSG_HDR)(&CTLHdr->QMICTLType));
|
||||||
|
}
|
||||||
|
|
||||||
|
int dump_qmux(QMI_SERVICE_TYPE serviceType, PQCQMUX_HDR QMUXHdr)
|
||||||
|
{
|
||||||
|
PQCQMUX_MSG_HDR QMUXMsgHdr = (PQCQMUX_MSG_HDR)(QMUXHdr + 1);
|
||||||
|
CHAR *tag;
|
||||||
|
|
||||||
|
// dbg("QCQMUX--------------------------------------------\n");
|
||||||
|
switch (QMUXHdr->CtlFlags & QMUX_CTL_FLAG_MASK_TYPE) {
|
||||||
|
case QMUX_CTL_FLAG_TYPE_CMD:
|
||||||
|
tag = "_REQ";
|
||||||
|
break;
|
||||||
|
case QMUX_CTL_FLAG_TYPE_RSP:
|
||||||
|
tag = "_RESP";
|
||||||
|
break;
|
||||||
|
case QMUX_CTL_FLAG_TYPE_IND:
|
||||||
|
tag = "_IND";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
tag = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// dbg("CtlFlags: %02x\t\t%s\n", QMUXHdr->CtlFlags,
|
||||||
|
// QMUX_NAME(qmux_CtlFlags, QMUXHdr->CtlFlags, tag));
|
||||||
|
dbg("TransactionId: %04x\n", le16_to_cpu(QMUXHdr->TransactionId));
|
||||||
|
|
||||||
|
// dbg("QCQMUX_MSG_HDR-----------------------------------\n");
|
||||||
|
switch (serviceType) {
|
||||||
|
case QMUX_TYPE_DMS:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_dms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_NAS:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_nas_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WDS:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_wds_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WMS:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_wms_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WDS_ADMIN:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_wds_admin_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_UIM:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
QMUX_NAME(qmux_uim_Type, le16_to_cpu(QMUXMsgHdr->Type), tag));
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_PDS:
|
||||||
|
case QMUX_TYPE_QOS:
|
||||||
|
case QMUX_TYPE_CTL:
|
||||||
|
default:
|
||||||
|
dbg("Type: %04x\t%s\n", le16_to_cpu(QMUXMsgHdr->Type),
|
||||||
|
"PDS/QOS/CTL/unknown!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dbg("Length: %04x\n", le16_to_cpu(QMUXMsgHdr->Length));
|
||||||
|
|
||||||
|
dump_tlv(QMUXMsgHdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_qmi(void *dataBuffer, int dataLen)
|
||||||
|
{
|
||||||
|
PQCQMI_HDR QMIHdr = (PQCQMI_HDR)dataBuffer;
|
||||||
|
PQCQMUX_HDR QMUXHdr = (PQCQMUX_HDR)(QMIHdr + 1);
|
||||||
|
PQCQMICTL_MSG_HDR CTLHdr = (PQCQMICTL_MSG_HDR)(QMIHdr + 1);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!debug_qmi)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&dumpQMIMutex);
|
||||||
|
line[0] = 0;
|
||||||
|
for (i = 0; i < dataLen; i++) {
|
||||||
|
dbg("%02x ", ((unsigned char *)dataBuffer)[i]);
|
||||||
|
}
|
||||||
|
dbg_time("%s", line);
|
||||||
|
line[0] = 0;
|
||||||
|
|
||||||
|
// dbg("QCQMI_HDR-----------------------------------------");
|
||||||
|
// dbg("IFType: %02x\t\t%s", QMIHdr->IFType,
|
||||||
|
// QMI_NAME(qmi_IFType, QMIHdr->IFType)); dbg("Length: %04x",
|
||||||
|
// le16_to_cpu(QMIHdr->Length)); dbg("CtlFlags: %02x\t\t%s",
|
||||||
|
// QMIHdr->CtlFlags, QMI_NAME(qmi_CtlFlags, QMIHdr->CtlFlags));
|
||||||
|
// dbg("QMIType: %02x\t\t%s", QMIHdr->QMIType, QMI_NAME(qmi_QMIType,
|
||||||
|
// QMIHdr->QMIType)); dbg("ClientId: %02x", QMIHdr->ClientId);
|
||||||
|
|
||||||
|
if (QMIHdr->QMIType == QMUX_TYPE_CTL) {
|
||||||
|
dump_ctl(CTLHdr);
|
||||||
|
} else {
|
||||||
|
dump_qmux(QMIHdr->QMIType, QMUXHdr);
|
||||||
|
}
|
||||||
|
dbg_time("%s", line);
|
||||||
|
pthread_mutex_unlock(&dumpQMIMutex);
|
||||||
|
}
|
3671
package/wwan/app/fibocom-dial/src/MPQMUX.h
Executable file
3671
package/wwan/app/fibocom-dial/src/MPQMUX.h
Executable file
File diff suppressed because it is too large
Load Diff
42
package/wwan/app/fibocom-dial/src/Makefile
Executable file
42
package/wwan/app/fibocom-dial/src/Makefile
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
ifneq ($(CROSS_COMPILE),)
|
||||||
|
CROSS-COMPILE:=$(CROSS_COMPILE)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS += -DGHT_FEATURE_PCIE_AUTO
|
||||||
|
|
||||||
|
ifeq ($(CC),cc)
|
||||||
|
CC:=$(CROSS-COMPILE)gcc
|
||||||
|
endif
|
||||||
|
LD:=$(CROSS-COMPILE)ld
|
||||||
|
|
||||||
|
SRC=QmiWwanCM.c GobiNetCM.c main.c MPQMUX.c QMIThread.c util.c qmap_bridge_mode.c query_pcie_mode.c
|
||||||
|
|
||||||
|
FB_DHCP=udhcpc.c
|
||||||
|
|
||||||
|
FIBO_PROXY_SRC=fibo_qmimsg_server.c
|
||||||
|
|
||||||
|
LIBMNL=libmnl/ifutils.c libmnl/attr.c libmnl/callback.c libmnl/nlmsg.c libmnl/socket.c
|
||||||
|
FB_NDHCP=udhcpc_netlink.c
|
||||||
|
FB_NDHCP+=${LIBMNL}
|
||||||
|
|
||||||
|
release: clean
|
||||||
|
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl
|
||||||
|
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl
|
||||||
|
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
|
||||||
|
|
||||||
|
dhcp: clean
|
||||||
|
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_DHCP} -o fibocom-dial -lpthread -ldl
|
||||||
|
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.c util.c -o multi-pdn-manager -lpthread -ldl
|
||||||
|
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
|
||||||
|
|
||||||
|
ndhcp: clean
|
||||||
|
$(CC) $(CFLAGS) -Wall -s ${SRC} ${FB_NDHCP} -o fibocom-dial -lpthread -ldl
|
||||||
|
$(CC) -Wall -s multi-pdn-manager.c query_pcie_mode.cutil.c -o multi-pdn-manager -lpthread -ldl
|
||||||
|
$(CC) -Wall -s ${FIBO_PROXY_SRC} -o fibo_qmimsg_server -lpthread -ldl
|
||||||
|
|
||||||
|
qmi-proxy:
|
||||||
|
$(CC) -Wall -s fibo-qmi-proxy.c -o fibo-qmi-proxy -lpthread -ldl
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf fibocom-dial *~ multi-pdn-manager fibo_qmimsg_server
|
||||||
|
|
2551
package/wwan/app/fibocom-dial/src/QMIThread.c
Executable file
2551
package/wwan/app/fibocom-dial/src/QMIThread.c
Executable file
File diff suppressed because it is too large
Load Diff
260
package/wwan/app/fibocom-dial/src/QMIThread.h
Executable file
260
package/wwan/app/fibocom-dial/src/QMIThread.h
Executable file
@ -0,0 +1,260 @@
|
|||||||
|
#ifndef __QMI_THREAD_H__
|
||||||
|
#define __QMI_THREAD_H__
|
||||||
|
|
||||||
|
#define CONFIG_GOBINET
|
||||||
|
#define CONFIG_QMIWWAN
|
||||||
|
#define CONFIG_SIM
|
||||||
|
#define CONFIG_APN
|
||||||
|
#define CONFIG_VERSION
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
|
||||||
|
#define CONFIG_IMSI_ICCID
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
|
||||||
|
#define CONFIG_DEFAULT_PDP 1
|
||||||
|
#define CONFIG_DEFAULT_PDPINDEX 1
|
||||||
|
//#define CONFIG_IMSI_ICCID
|
||||||
|
|
||||||
|
#define CONFIG_RESET_RADIO \
|
||||||
|
(45) // Reset Radiao(AT+CFUN=4,AT+CFUN=1) when cann not register network or
|
||||||
|
// setup data call in 45 seconds
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "MPQCTL.h"
|
||||||
|
#include "MPQMI.h"
|
||||||
|
#include "MPQMUX.h"
|
||||||
|
|
||||||
|
#define DEVICE_CLASS_UNKNOWN 0
|
||||||
|
#define DEVICE_CLASS_CDMA 1
|
||||||
|
#define DEVICE_CLASS_GSM 2
|
||||||
|
|
||||||
|
#define WWAN_DATA_CLASS_NONE 0x00000000
|
||||||
|
#define WWAN_DATA_CLASS_GPRS 0x00000001
|
||||||
|
#define WWAN_DATA_CLASS_EDGE 0x00000002 /* EGPRS */
|
||||||
|
#define WWAN_DATA_CLASS_UMTS 0x00000004
|
||||||
|
#define WWAN_DATA_CLASS_HSDPA 0x00000008
|
||||||
|
#define WWAN_DATA_CLASS_HSUPA 0x00000010
|
||||||
|
#define WWAN_DATA_CLASS_LTE 0x00000020
|
||||||
|
//begin modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605
|
||||||
|
#define WWAN_DATA_CLASS_5G 0x00000040
|
||||||
|
//end modified by zhangkaibo add 5G network detect feature on x55 platform. 20200605
|
||||||
|
#define WWAN_DATA_CLASS_1XRTT 0x00010000
|
||||||
|
#define WWAN_DATA_CLASS_1XEVDO 0x00020000
|
||||||
|
#define WWAN_DATA_CLASS_1XEVDO_REVA 0x00040000
|
||||||
|
#define WWAN_DATA_CLASS_1XEVDV 0x00080000
|
||||||
|
#define WWAN_DATA_CLASS_3XRTT 0x00100000
|
||||||
|
#define WWAN_DATA_CLASS_1XEVDO_REVB 0x00200000 /* for future use */
|
||||||
|
#define WWAN_DATA_CLASS_UMB 0x00400000
|
||||||
|
#define WWAN_DATA_CLASS_CUSTOM 0x80000000
|
||||||
|
|
||||||
|
struct wwan_data_class_str {
|
||||||
|
ULONG class;
|
||||||
|
CHAR *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
typedef struct _QCQMIMSG {
|
||||||
|
QCQMI_HDR QMIHdr;
|
||||||
|
union {
|
||||||
|
QMICTL_MSG CTLMsg;
|
||||||
|
QMUX_MSG MUXMsg;
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
|
||||||
|
QMUX_MSG QMUXMsgHdrResp;
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
|
||||||
|
};
|
||||||
|
} __attribute__((packed)) QCQMIMSG, *PQCQMIMSG;
|
||||||
|
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
typedef struct __IPV4 {
|
||||||
|
uint32_t Address;
|
||||||
|
uint32_t Gateway;
|
||||||
|
uint32_t SubnetMask;
|
||||||
|
uint32_t DnsPrimary;
|
||||||
|
uint32_t DnsSecondary;
|
||||||
|
uint32_t Mtu;
|
||||||
|
} IPV4_T;
|
||||||
|
|
||||||
|
typedef struct __IPV6 {
|
||||||
|
UCHAR Address[16];
|
||||||
|
UCHAR Gateway[16];
|
||||||
|
UCHAR SubnetMask[16];
|
||||||
|
UCHAR DnsPrimary[16];
|
||||||
|
UCHAR DnsSecondary[16];
|
||||||
|
UCHAR PrefixLengthIPAddr;
|
||||||
|
UCHAR PrefixLengthGateway;
|
||||||
|
ULONG Mtu;
|
||||||
|
} IPV6_T;
|
||||||
|
|
||||||
|
#define IpFamilyV4 (0x04)
|
||||||
|
#define IpFamilyV6 (0x06)
|
||||||
|
|
||||||
|
struct __PROFILE;
|
||||||
|
struct qmi_device_ops {
|
||||||
|
int (*init)(struct __PROFILE *profile);
|
||||||
|
int (*deinit)(void);
|
||||||
|
int (*send)(PQCQMIMSG pRequest);
|
||||||
|
void *(*read)(void *pData);
|
||||||
|
};
|
||||||
|
extern int (*qmidev_send)(PQCQMIMSG pRequest);
|
||||||
|
|
||||||
|
#ifndef bool
|
||||||
|
#define bool uint8_t
|
||||||
|
#endif
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
|
||||||
|
typedef struct {
|
||||||
|
unsigned int size;
|
||||||
|
unsigned int rx_urb_size;
|
||||||
|
unsigned int ep_type;
|
||||||
|
unsigned int iface_id;
|
||||||
|
unsigned int qmap_mode;
|
||||||
|
unsigned int qmap_version;
|
||||||
|
unsigned int dl_minimum_padding;
|
||||||
|
char ifname[8][16];
|
||||||
|
unsigned char mux_id[8];
|
||||||
|
} RMNET_INFO;
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
|
||||||
|
|
||||||
|
typedef struct __PROFILE {
|
||||||
|
char *qmichannel;
|
||||||
|
char *usbnet_adapter;
|
||||||
|
char *qmapnet_adapter;
|
||||||
|
const char *driver_name;
|
||||||
|
int qmap_mode;
|
||||||
|
int qmap_size;
|
||||||
|
int qmap_version;
|
||||||
|
const char *apn;
|
||||||
|
const char *user;
|
||||||
|
const char *password;
|
||||||
|
const char *pincode;
|
||||||
|
int auth;
|
||||||
|
int pdp;
|
||||||
|
int pdpindex;
|
||||||
|
int pdpnum;
|
||||||
|
int curIpFamily;
|
||||||
|
int rawIP;
|
||||||
|
int muxid;
|
||||||
|
IPV4_T ipv4;
|
||||||
|
IPV6_T ipv6;
|
||||||
|
int ipv4_flag;
|
||||||
|
int ipv6_flag;
|
||||||
|
|
||||||
|
//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
|
||||||
|
int ipv6_prigateway_flag;
|
||||||
|
//2021-02-25 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
|
||||||
|
|
||||||
|
int dual_flag;
|
||||||
|
int apntype;
|
||||||
|
const struct qmi_device_ops *qmi_ops;
|
||||||
|
bool loopback_state;
|
||||||
|
int replication_factor;
|
||||||
|
//2021-02-08 zhangkaibo@fibocom.com changed begin for mantis 0070613
|
||||||
|
int interfacenum;
|
||||||
|
//2021-02-08 zhangkaibo@fibocom.com changed end for mantis 0070613
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
|
||||||
|
RMNET_INFO rmnet_info;
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
|
||||||
|
} PROFILE_T;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SIM_ABSENT = 0,
|
||||||
|
SIM_NOT_READY = 1,
|
||||||
|
SIM_READY =
|
||||||
|
2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
|
||||||
|
SIM_PIN = 3,
|
||||||
|
SIM_PUK = 4,
|
||||||
|
SIM_NETWORK_PERSONALIZATION = 5,
|
||||||
|
SIM_BAD = 6,
|
||||||
|
} SIM_Status;
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
|
||||||
|
typedef enum {
|
||||||
|
SIM_Card0 = 0,
|
||||||
|
SIM_Card1 = 1
|
||||||
|
} SIM_Select;
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
|
||||||
|
#define WDM_DEFAULT_BUFSIZE 256
|
||||||
|
#define RIL_REQUEST_QUIT 0x1000
|
||||||
|
#define RIL_INDICATE_DEVICE_CONNECTED 0x1002
|
||||||
|
#define RIL_INDICATE_DEVICE_DISCONNECTED 0x1003
|
||||||
|
#define RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED 0x1004
|
||||||
|
#define RIL_UNSOL_DATA_CALL_LIST_CHANGED 0x1005
|
||||||
|
|
||||||
|
extern int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
|
unsigned msecs);
|
||||||
|
extern int QmiThreadSendQMI(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse);
|
||||||
|
extern int QmiThreadSendQMITimeout(PQCQMIMSG pRequest, PQCQMIMSG *ppResponse,
|
||||||
|
unsigned msecs);
|
||||||
|
extern void QmiThreadRecvQMI(PQCQMIMSG pResponse);
|
||||||
|
extern int fibo_raw_ip_mode_check(const char *ifname);
|
||||||
|
extern void udhcpc_start(PROFILE_T *profile);
|
||||||
|
extern void udhcpc_stop(PROFILE_T *profile);
|
||||||
|
extern void udhcpc_start_pcie(PROFILE_T *profile);
|
||||||
|
extern void udhcpc_stop_pcie(PROFILE_T *profile);
|
||||||
|
|
||||||
|
extern void dump_qmi(void *dataBuffer, int dataLen);
|
||||||
|
extern void qmidevice_send_event_to_main(int triger_event);
|
||||||
|
extern int requestSetEthMode(PROFILE_T *profile);
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
|
||||||
|
//extern int requestGetSIMStatus(SIM_Status *pSIMStatus);
|
||||||
|
extern int requestGetSIMStatus(SIM_Status *pSIMStatus , const int sim_select);
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
|
||||||
|
extern int requestEnterSimPin(const CHAR *pPinCode);
|
||||||
|
extern int requestGetICCID(void);
|
||||||
|
extern int requestGetIMSI(void);
|
||||||
|
extern int requestRegistrationState(UCHAR *pPSAttachedState);
|
||||||
|
extern int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily);
|
||||||
|
extern int requestSetupDataCall(PROFILE_T *profile, int curIpFamily);
|
||||||
|
extern int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily);
|
||||||
|
extern int requestSetProfile(PROFILE_T *profile);
|
||||||
|
extern int requestGetProfile(PROFILE_T *profile);
|
||||||
|
extern int requestBaseBandVersion(const char **pp_reversion);
|
||||||
|
extern int requestGetIPAddress(PROFILE_T *profile, int curIpFamily);
|
||||||
|
extern int requestSetOperatingMode(UCHAR OperatingMode);
|
||||||
|
int requestRegistrationState2(UCHAR *pPSAttachedState);
|
||||||
|
|
||||||
|
extern int fibo_qmap_mode_set(PROFILE_T *profile);
|
||||||
|
extern int fibo_bridge_mode_detect(PROFILE_T *profile);
|
||||||
|
extern int fibo_qmap_mode_detect(PROFILE_T *profile);
|
||||||
|
extern const struct qmi_device_ops qmiwwan_qmidev_ops;
|
||||||
|
|
||||||
|
#define qmidev_is_gobinet(_qmichannel) \
|
||||||
|
(strncmp(_qmichannel, "/dev/qcqmi", strlen("/dev/qcqmi")) == 0)
|
||||||
|
#define qmidev_is_qmiwwan(_qmichannel) \
|
||||||
|
(strncmp(_qmichannel, "/dev/cdc-wdm", strlen("/dev/cdc-wdm")) == 0)
|
||||||
|
#define qmidev_is_pciemhi(_qmichannel) \
|
||||||
|
(strncmp(_qmichannel, "/dev/mhi_", strlen("/dev/mhi_")) == 0)
|
||||||
|
|
||||||
|
#define driver_is_qmi(_drv_name) \
|
||||||
|
(strncasecmp(_drv_name, "qmi_wwan", strlen("qmi_wwan")) == 0)
|
||||||
|
#define driver_is_mbim(_drv_name) \
|
||||||
|
(strncasecmp(_drv_name, "cdc_mbim", strlen("cdc_mbim")) == 0)
|
||||||
|
|
||||||
|
extern FILE *logfilefp;
|
||||||
|
extern int debug_qmi;
|
||||||
|
extern USHORT g_MobileCountryCode;
|
||||||
|
extern USHORT g_MobileNetworkCode;
|
||||||
|
extern int qmidevice_control_fd[2];
|
||||||
|
extern int qmiclientId[QMUX_TYPE_WDS_ADMIN + 1];
|
||||||
|
extern void dbg_time(const char *fmt, ...);
|
||||||
|
extern USHORT le16_to_cpu(USHORT v16);
|
||||||
|
extern UINT le32_to_cpu(UINT v32);
|
||||||
|
extern USHORT cpu_to_le16(USHORT v16);
|
||||||
|
extern UINT cpu_to_le32(UINT v32);
|
||||||
|
#endif
|
426
package/wwan/app/fibocom-dial/src/QmiWwanCM.c
Executable file
426
package/wwan/app/fibocom-dial/src/QmiWwanCM.c
Executable file
@ -0,0 +1,426 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <termios.h>
|
||||||
|
typedef unsigned short sa_family_t;
|
||||||
|
#include <linux/un.h>
|
||||||
|
|
||||||
|
#include "QMIThread.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_QMIWWAN
|
||||||
|
#define FIBO_QMI_PROXY "fibo_qmimsg_server"
|
||||||
|
static int cdc_wdm_fd = -1;
|
||||||
|
|
||||||
|
static UCHAR GetQCTLTransactionId(void)
|
||||||
|
{
|
||||||
|
static int TransactionId = 0;
|
||||||
|
if (++TransactionId > 0xFF)
|
||||||
|
TransactionId = 1;
|
||||||
|
return TransactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef USHORT (*CUSTOMQCTL)(PQMICTL_MSG pCTLMsg, void *arg);
|
||||||
|
|
||||||
|
static PQCQMIMSG ComposeQCTLMsg(USHORT QMICTLType,
|
||||||
|
CUSTOMQCTL customQctlMsgFunction, void *arg)
|
||||||
|
{
|
||||||
|
UCHAR QMIBuf[WDM_DEFAULT_BUFSIZE];
|
||||||
|
PQCQMIMSG pRequest = (PQCQMIMSG)QMIBuf;
|
||||||
|
int Length;
|
||||||
|
|
||||||
|
pRequest->QMIHdr.IFType = USB_CTL_MSG_TYPE_QMI;
|
||||||
|
pRequest->QMIHdr.CtlFlags = 0x00;
|
||||||
|
pRequest->QMIHdr.QMIType = QMUX_TYPE_CTL;
|
||||||
|
pRequest->QMIHdr.ClientId = 0x00;
|
||||||
|
|
||||||
|
pRequest->CTLMsg.QMICTLMsgHdr.CtlFlags = QMICTL_FLAG_REQUEST;
|
||||||
|
pRequest->CTLMsg.QMICTLMsgHdr.TransactionId = GetQCTLTransactionId();
|
||||||
|
pRequest->CTLMsg.QMICTLMsgHdr.QMICTLType = cpu_to_le16(QMICTLType);
|
||||||
|
if (customQctlMsgFunction)
|
||||||
|
pRequest->CTLMsg.QMICTLMsgHdr.Length =
|
||||||
|
cpu_to_le16(customQctlMsgFunction(&pRequest->CTLMsg, arg) -
|
||||||
|
sizeof(QCQMICTL_MSG_HDR));
|
||||||
|
else
|
||||||
|
pRequest->CTLMsg.QMICTLMsgHdr.Length = cpu_to_le16(0x0000);
|
||||||
|
|
||||||
|
pRequest->QMIHdr.Length =
|
||||||
|
cpu_to_le16(le16_to_cpu(pRequest->CTLMsg.QMICTLMsgHdr.Length) +
|
||||||
|
sizeof(QCQMICTL_MSG_HDR) + sizeof(QCQMI_HDR) - 1);
|
||||||
|
Length = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
|
||||||
|
|
||||||
|
pRequest = (PQCQMIMSG)malloc(Length);
|
||||||
|
if (pRequest == NULL) {
|
||||||
|
dbg_time("%s fail to malloc", __func__);
|
||||||
|
} else {
|
||||||
|
memcpy(pRequest, QMIBuf, Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static USHORT CtlGetVersionReq(PQMICTL_MSG QCTLMsg, void *arg)
|
||||||
|
{
|
||||||
|
QCTLMsg->GetVersionReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
|
||||||
|
QCTLMsg->GetVersionReq.TLVLength = cpu_to_le16(0x0001);
|
||||||
|
QCTLMsg->GetVersionReq.QMUXTypes = QMUX_TYPE_ALL;
|
||||||
|
return sizeof(QMICTL_GET_VERSION_REQ_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static USHORT CtlGetClientIdReq(PQMICTL_MSG QCTLMsg, void *arg)
|
||||||
|
{
|
||||||
|
QCTLMsg->GetClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
|
||||||
|
QCTLMsg->GetClientIdReq.TLVLength = cpu_to_le16(0x0001);
|
||||||
|
QCTLMsg->GetClientIdReq.QMIType = ((UCHAR *)arg)[0];
|
||||||
|
return sizeof(QMICTL_GET_CLIENT_ID_REQ_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static USHORT CtlReleaseClientIdReq(PQMICTL_MSG QCTLMsg, void *arg)
|
||||||
|
{
|
||||||
|
QCTLMsg->ReleaseClientIdReq.TLVType = QCTLV_TYPE_REQUIRED_PARAMETER;
|
||||||
|
QCTLMsg->ReleaseClientIdReq.TLVLength = cpu_to_le16(0x0002);
|
||||||
|
QCTLMsg->ReleaseClientIdReq.QMIType = ((UCHAR *)arg)[0];
|
||||||
|
QCTLMsg->ReleaseClientIdReq.ClientId = ((UCHAR *)arg)[1];
|
||||||
|
return sizeof(QMICTL_RELEASE_CLIENT_ID_REQ_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int QmiWwanSendQMI(PQCQMIMSG pRequest)
|
||||||
|
{
|
||||||
|
struct pollfd pollfds[] = {{cdc_wdm_fd, POLLOUT, 0}};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (cdc_wdm_fd == -1) {
|
||||||
|
dbg_time("%s cdc_wdm_fd = -1", __func__);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pRequest->QMIHdr.QMIType == QMUX_TYPE_WDS_IPV6)
|
||||||
|
pRequest->QMIHdr.QMIType = QMUX_TYPE_WDS;
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), 5000);
|
||||||
|
} while ((ret < 0) && (errno == EINTR));
|
||||||
|
|
||||||
|
if (pollfds[0].revents & POLLOUT) {
|
||||||
|
ssize_t nwrites = le16_to_cpu(pRequest->QMIHdr.Length) + 1;
|
||||||
|
ret = write(cdc_wdm_fd, pRequest, nwrites);
|
||||||
|
if (ret == nwrites) {
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
dbg_time("%s write=%d, errno: %d (%s)", __func__, ret, errno,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dbg_time("%s poll=%d, revents = 0x%x, errno: %d (%s)", __func__, ret,
|
||||||
|
pollfds[0].revents, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int QmiWwanGetClientID(UCHAR QMIType)
|
||||||
|
{
|
||||||
|
PQCQMIMSG pResponse;
|
||||||
|
|
||||||
|
QmiThreadSendQMI(
|
||||||
|
ComposeQCTLMsg(QMICTL_GET_CLIENT_ID_REQ, CtlGetClientIdReq, &QMIType),
|
||||||
|
&pResponse);
|
||||||
|
|
||||||
|
if (pResponse) {
|
||||||
|
USHORT QMUXResult = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp
|
||||||
|
.QMUXResult); // QMI_RESULT_SUCCESS
|
||||||
|
USHORT QMUXError = cpu_to_le16(pResponse->CTLMsg.QMICTLMsgHdrRsp
|
||||||
|
.QMUXError); // QMI_ERR_INVALID_ARG
|
||||||
|
// UCHAR QMIType = pResponse->CTLMsg.GetClientIdRsp.QMIType;
|
||||||
|
UCHAR ClientId = pResponse->CTLMsg.GetClientIdRsp.ClientId;
|
||||||
|
|
||||||
|
dbg_time("%s: QMIType = %d clientid %d", __func__, QMIType, ClientId);
|
||||||
|
|
||||||
|
if (!QMUXResult && !QMUXError &&
|
||||||
|
(QMIType == pResponse->CTLMsg.GetClientIdRsp.QMIType)) {
|
||||||
|
switch (QMIType) {
|
||||||
|
case QMUX_TYPE_WDS:
|
||||||
|
dbg_time("Get clientWDS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_DMS:
|
||||||
|
dbg_time("Get clientDMS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_NAS:
|
||||||
|
dbg_time("Get clientNAS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_QOS:
|
||||||
|
dbg_time("Get clientQOS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WMS:
|
||||||
|
dbg_time("Get clientWMS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_PDS:
|
||||||
|
dbg_time("Get clientPDS = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_UIM:
|
||||||
|
dbg_time("Get clientUIM = %d", ClientId);
|
||||||
|
break;
|
||||||
|
case QMUX_TYPE_WDS_ADMIN:
|
||||||
|
dbg_time("Get clientWDA = %d", ClientId);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ClientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int QmiWwanReleaseClientID(QMI_SERVICE_TYPE QMIType, UCHAR ClientId)
|
||||||
|
{
|
||||||
|
UCHAR argv[] = {QMIType, ClientId};
|
||||||
|
QmiThreadSendQMI(ComposeQCTLMsg(QMICTL_RELEASE_CLIENT_ID_REQ,
|
||||||
|
CtlReleaseClientIdReq, argv),
|
||||||
|
NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int QmiWwanInit(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
int ret;
|
||||||
|
PQCQMIMSG pResponse;
|
||||||
|
|
||||||
|
if (profile->qmap_mode == 0 || profile->qmap_mode == 1) {
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
ret = QmiThreadSendQMITimeout(
|
||||||
|
ComposeQCTLMsg(QMICTL_SYNC_REQ, NULL, NULL), NULL, 1 * 1000);
|
||||||
|
if (!ret)
|
||||||
|
break;
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
QmiThreadSendQMI(
|
||||||
|
ComposeQCTLMsg(QMICTL_GET_VERSION_REQ, CtlGetVersionReq, NULL),
|
||||||
|
&pResponse);
|
||||||
|
if (profile->qmap_mode) {
|
||||||
|
if (pResponse) {
|
||||||
|
if (pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXResult == 0 &&
|
||||||
|
pResponse->CTLMsg.QMICTLMsgHdrRsp.QMUXError == 0) {
|
||||||
|
uint8_t NumElements = 0;
|
||||||
|
|
||||||
|
for (NumElements = 0;
|
||||||
|
NumElements < pResponse->CTLMsg.GetVersionRsp.NumElements;
|
||||||
|
NumElements++) {
|
||||||
|
if (pResponse->CTLMsg.GetVersionRsp.TypeVersion[NumElements]
|
||||||
|
.QMUXType == QMUX_TYPE_WDS_ADMIN)
|
||||||
|
profile->qmap_version = (pResponse->CTLMsg.GetVersionRsp
|
||||||
|
.TypeVersion[NumElements]
|
||||||
|
.MinorVersion > 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pResponse)
|
||||||
|
free(pResponse);
|
||||||
|
if (profile->ipv4_flag)
|
||||||
|
qmiclientId[QMUX_TYPE_WDS] = QmiWwanGetClientID(QMUX_TYPE_WDS);
|
||||||
|
if (profile->ipv6_flag)
|
||||||
|
qmiclientId[QMUX_TYPE_WDS_IPV6] = QmiWwanGetClientID(QMUX_TYPE_WDS);
|
||||||
|
qmiclientId[QMUX_TYPE_DMS] = QmiWwanGetClientID(QMUX_TYPE_DMS);
|
||||||
|
qmiclientId[QMUX_TYPE_NAS] = QmiWwanGetClientID(QMUX_TYPE_NAS);
|
||||||
|
qmiclientId[QMUX_TYPE_UIM] = QmiWwanGetClientID(QMUX_TYPE_UIM);
|
||||||
|
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
|
||||||
|
qmiclientId[QMUX_TYPE_WDS_ADMIN] =
|
||||||
|
QmiWwanGetClientID(QMUX_TYPE_WDS_ADMIN);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int QmiWwanDeInit(void)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < sizeof(qmiclientId) / sizeof(qmiclientId[0]); i++) {
|
||||||
|
if (qmiclientId[i] != 0) {
|
||||||
|
QmiWwanReleaseClientID(i, qmiclientId[i]);
|
||||||
|
qmiclientId[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qmi_proxy_open(const char *name)
|
||||||
|
{
|
||||||
|
int sockfd = -1;
|
||||||
|
int reuse_addr = 1;
|
||||||
|
struct sockaddr_un sockaddr;
|
||||||
|
socklen_t alen;
|
||||||
|
|
||||||
|
/*Create server socket*/
|
||||||
|
(sockfd = socket(AF_LOCAL, SOCK_STREAM, 0));
|
||||||
|
if (sockfd < 0)
|
||||||
|
return sockfd;
|
||||||
|
|
||||||
|
memset(&sockaddr, 0, sizeof(sockaddr));
|
||||||
|
sockaddr.sun_family = AF_LOCAL;
|
||||||
|
sockaddr.sun_path[0] = 0;
|
||||||
|
memcpy(sockaddr.sun_path + 1, name, strlen(name));
|
||||||
|
|
||||||
|
alen = strlen(name) + offsetof(struct sockaddr_un, sun_path) + 1;
|
||||||
|
if (connect(sockfd, (struct sockaddr *)&sockaddr, alen) < 0) {
|
||||||
|
close(sockfd);
|
||||||
|
dbg_time("%s connect %s errno: %d (%s)\n", __func__, name, errno,
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_addr,
|
||||||
|
sizeof(reuse_addr)));
|
||||||
|
|
||||||
|
dbg_time("connect to %s sockfd = %d\n", name, sockfd);
|
||||||
|
|
||||||
|
return sockfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t qmi_proxy_read(int fd, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
ssize_t nreads;
|
||||||
|
PQCQMI_HDR pHdr = (PQCQMI_HDR)buf;
|
||||||
|
|
||||||
|
nreads = read(fd, pHdr, sizeof(QCQMI_HDR));
|
||||||
|
if (nreads == sizeof(QCQMI_HDR)) {
|
||||||
|
nreads += read(fd, pHdr + 1,
|
||||||
|
le16_to_cpu(pHdr->Length) + 1 - sizeof(QCQMI_HDR));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *QmiWwanThread(void *pData)
|
||||||
|
{
|
||||||
|
PROFILE_T *profile = (PROFILE_T *)pData;
|
||||||
|
const char *cdc_wdm = (const char *)profile->qmichannel;
|
||||||
|
|
||||||
|
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
|
||||||
|
cdc_wdm_fd = open(cdc_wdm, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
else
|
||||||
|
cdc_wdm_fd = qmi_proxy_open(FIBO_QMI_PROXY);
|
||||||
|
|
||||||
|
if (cdc_wdm_fd == -1) {
|
||||||
|
dbg_time("%s Failed to open %s, errno: %d (%s)", __func__, cdc_wdm,
|
||||||
|
errno, strerror(errno));
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fcntl(cdc_wdm_fd, F_SETFL, fcntl(cdc_wdm_fd, F_GETFL) | O_NONBLOCK);
|
||||||
|
fcntl(cdc_wdm_fd, F_SETFD, FD_CLOEXEC);
|
||||||
|
|
||||||
|
dbg_time("cdc_wdm_fd = %d", cdc_wdm_fd);
|
||||||
|
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct pollfd pollfds[] = {{qmidevice_control_fd[1], POLLIN, 0},
|
||||||
|
{cdc_wdm_fd, POLLIN, 0}};
|
||||||
|
int ne, ret, nevents = sizeof(pollfds) / sizeof(pollfds[0]);
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = poll(pollfds, nevents, -1);
|
||||||
|
} while ((ret < 0) && (errno == EINTR));
|
||||||
|
|
||||||
|
if (ret <= 0) {
|
||||||
|
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno,
|
||||||
|
strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ne = 0; ne < nevents; ne++) {
|
||||||
|
int fd = pollfds[ne].fd;
|
||||||
|
short revents = pollfds[ne].revents;
|
||||||
|
|
||||||
|
// dbg_time("{%d, %x, %x}", pollfds[ne].fd, pollfds[ne].events,
|
||||||
|
// pollfds[ne].revents);
|
||||||
|
|
||||||
|
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||||
|
dbg_time("%s poll err/hup/inval", __func__);
|
||||||
|
dbg_time("poll fd = %d, events = 0x%04x", fd, revents);
|
||||||
|
if (fd == cdc_wdm_fd) {
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
if (revents &
|
||||||
|
(POLLHUP | POLLNVAL)) // EC20 bug, Can get POLLERR
|
||||||
|
goto __QmiWwanThread_quit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((revents & POLLIN) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (fd == qmidevice_control_fd[1]) {
|
||||||
|
int triger_event;
|
||||||
|
if (read(fd, &triger_event, sizeof(triger_event)) ==
|
||||||
|
sizeof(triger_event)) {
|
||||||
|
// DBG("triger_event = 0x%x", triger_event);
|
||||||
|
switch (triger_event) {
|
||||||
|
case RIL_REQUEST_QUIT:
|
||||||
|
goto __QmiWwanThread_quit;
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGHUP:
|
||||||
|
case SIGINT:
|
||||||
|
QmiThreadRecvQMI(NULL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd == cdc_wdm_fd) {
|
||||||
|
ssize_t nreads;
|
||||||
|
UCHAR QMIBuf[4096];
|
||||||
|
PQCQMIMSG pResponse = (PQCQMIMSG)QMIBuf;
|
||||||
|
|
||||||
|
if (profile->qmap_mode == 0 || profile->qmap_mode == 1)
|
||||||
|
nreads = read(fd, QMIBuf, sizeof(QMIBuf));
|
||||||
|
else
|
||||||
|
nreads = qmi_proxy_read(fd, QMIBuf, sizeof(QMIBuf));
|
||||||
|
// dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
|
||||||
|
// errno, strerror(errno));
|
||||||
|
if (nreads <= 0) {
|
||||||
|
dbg_time("%s read=%d errno: %d (%s)", __func__, (int)nreads,
|
||||||
|
errno, strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nreads != (le16_to_cpu(pResponse->QMIHdr.Length) + 1)) {
|
||||||
|
dbg_time("%s nreads=%d, pQCQMI->QMIHdr.Length = %d",
|
||||||
|
__func__, (int)nreads,
|
||||||
|
le16_to_cpu(pResponse->QMIHdr.Length));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QmiThreadRecvQMI(pResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__QmiWwanThread_quit:
|
||||||
|
if (cdc_wdm_fd != -1) {
|
||||||
|
close(cdc_wdm_fd);
|
||||||
|
cdc_wdm_fd = -1;
|
||||||
|
}
|
||||||
|
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
|
||||||
|
QmiThreadRecvQMI(NULL); // main thread may pending on QmiThreadSendQMI()
|
||||||
|
dbg_time("%s exit", __func__);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const struct qmi_device_ops qmiwwan_qmidev_ops = {
|
||||||
|
.init = QmiWwanInit,
|
||||||
|
.deinit = QmiWwanDeInit,
|
||||||
|
.send = QmiWwanSendQMI,
|
||||||
|
.read = QmiWwanThread,
|
||||||
|
};
|
0
package/wwan/driver/quectel_cm_5G/src/default.script → package/wwan/app/fibocom-dial/src/default.script
Normal file → Executable file
0
package/wwan/driver/quectel_cm_5G/src/default.script → package/wwan/app/fibocom-dial/src/default.script
Normal file → Executable file
1331
package/wwan/app/fibocom-dial/src/fibo_qmimsg_server.c
Executable file
1331
package/wwan/app/fibocom-dial/src/fibo_qmimsg_server.c
Executable file
File diff suppressed because it is too large
Load Diff
28
package/wwan/app/fibocom-dial/src/libmnl/README
Executable file
28
package/wwan/app/fibocom-dial/src/libmnl/README
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
= What is libmnl? =
|
||||||
|
|
||||||
|
libmnl is a minimalistic user-space library oriented to Netlink developers.
|
||||||
|
There are a lot of common tasks in parsing, validating, constructing of
|
||||||
|
both the Netlink header and TLVs that are repetitive and easy to get wrong.
|
||||||
|
This library aims to provide simple helpers that allows you to re-use code
|
||||||
|
and to avoid re-inventing the wheel. The main features of this library are:
|
||||||
|
|
||||||
|
* Small: the shared library requires around 30KB for an x86-based computer.
|
||||||
|
* Simple: this library avoids complexity and elaborated abstractions that
|
||||||
|
tend to hide Netlink details.
|
||||||
|
* Easy to use: the library simplifies the work for Netlink-wise developers.
|
||||||
|
It provides functions to make socket handling, message building, validating,
|
||||||
|
parsing and sequence tracking, easier.
|
||||||
|
* Easy to re-use: you can use the library to build your own abstraction layer
|
||||||
|
on top of this library.
|
||||||
|
* Decoupling: the interdependency of the main bricks that compose the library
|
||||||
|
is reduced, i.e. the library provides many helpers, but the programmer is not
|
||||||
|
forced to use them.
|
||||||
|
|
||||||
|
= Example files =
|
||||||
|
|
||||||
|
You can find several example files under examples/ that you can compile by
|
||||||
|
invoking `make check'.
|
||||||
|
|
||||||
|
--
|
||||||
|
08/sep/2010
|
||||||
|
Pablo Neira Ayuso <pablo@netfilter.org>
|
722
package/wwan/app/fibocom-dial/src/libmnl/attr.c
Executable file
722
package/wwan/app/fibocom-dial/src/libmnl/attr.c
Executable file
@ -0,0 +1,722 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2008-2012 by Pablo Neira Ayuso <pablo@netfilter.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
#include <limits.h> /* for INT_MAX */
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "libmnl.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup attr Netlink attribute helpers
|
||||||
|
*
|
||||||
|
* Netlink Type-Length-Value (TLV) attribute:
|
||||||
|
* \verbatim
|
||||||
|
|<-- 2 bytes -->|<-- 2 bytes -->|<-- variable -->|
|
||||||
|
-------------------------------------------------
|
||||||
|
| length | type | value |
|
||||||
|
-------------------------------------------------
|
||||||
|
|<--------- header ------------>|<-- payload --->|
|
||||||
|
\endverbatim
|
||||||
|
* The payload of the Netlink message contains sequences of attributes that are
|
||||||
|
* expressed in TLV format.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_type - get type of netlink attribute
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the attribute type.
|
||||||
|
*/
|
||||||
|
uint16_t mnl_attr_get_type(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return attr->nla_type & NLA_TYPE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_len - get length of netlink attribute
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the attribute length that is the attribute header
|
||||||
|
* plus the attribute payload.
|
||||||
|
*/
|
||||||
|
uint16_t mnl_attr_get_len(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return attr->nla_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_payload_len - get the attribute payload-value length
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the attribute payload-value length.
|
||||||
|
*/
|
||||||
|
uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return attr->nla_len - MNL_ATTR_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_payload - get pointer to the attribute payload
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function return a pointer to the attribute payload.
|
||||||
|
*/
|
||||||
|
void *mnl_attr_get_payload(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return (void *)attr + MNL_ATTR_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_ok - check if there is room for an attribute in a buffer
|
||||||
|
* \param attr attribute that we want to check if there is room for
|
||||||
|
* \param len remaining bytes in a buffer that contains the attribute
|
||||||
|
*
|
||||||
|
* This function is used to check that a buffer, which is supposed to contain
|
||||||
|
* an attribute, has enough room for the attribute that it stores, i.e. this
|
||||||
|
* function can be used to verify that an attribute is neither malformed nor
|
||||||
|
* truncated.
|
||||||
|
*
|
||||||
|
* This function does not set errno in case of error since it is intended
|
||||||
|
* for iterations. Thus, it returns true on success and false on error.
|
||||||
|
*
|
||||||
|
* The len parameter may be negative in the case of malformed messages during
|
||||||
|
* attribute iteration, that is why we use a signed integer.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_ok(const struct nlattr *attr, int len)
|
||||||
|
{
|
||||||
|
return len >= (int)sizeof(struct nlattr) &&
|
||||||
|
attr->nla_len >= sizeof(struct nlattr) &&
|
||||||
|
(int)attr->nla_len <= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_next - get the next attribute in the payload of a netlink message
|
||||||
|
* \param attr pointer to the current attribute
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the next attribute after the one passed
|
||||||
|
* as parameter. You have to use mnl_attr_ok() to ensure that the next
|
||||||
|
* attribute is valid.
|
||||||
|
*/
|
||||||
|
struct nlattr *mnl_attr_next(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_type_valid - check if the attribute type is valid
|
||||||
|
* \param attr pointer to attribute to be checked
|
||||||
|
* \param max maximum attribute type
|
||||||
|
*
|
||||||
|
* This function allows to check if the attribute type is higher than the
|
||||||
|
* maximum supported type. If the attribute type is invalid, this function
|
||||||
|
* returns -1 and errno is explicitly set. On success, this function returns 1.
|
||||||
|
*
|
||||||
|
* Strict attribute checking in user-space is not a good idea since you may
|
||||||
|
* run an old application with a newer kernel that supports new attributes.
|
||||||
|
* This leads to backward compatibility breakages in user-space. Better check
|
||||||
|
* if you support an attribute, if not, skip it.
|
||||||
|
*/
|
||||||
|
int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)
|
||||||
|
{
|
||||||
|
if (mnl_attr_get_type(attr) > max) {
|
||||||
|
errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __mnl_attr_validate(const struct nlattr *attr,
|
||||||
|
enum mnl_attr_data_type type, size_t exp_len)
|
||||||
|
{
|
||||||
|
uint16_t attr_len = mnl_attr_get_payload_len(attr);
|
||||||
|
const char *attr_data = mnl_attr_get_payload(attr);
|
||||||
|
|
||||||
|
if (attr_len < exp_len) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
switch(type) {
|
||||||
|
case MNL_TYPE_FLAG:
|
||||||
|
if (attr_len > 0) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MNL_TYPE_NUL_STRING:
|
||||||
|
if (attr_len == 0) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (attr_data[attr_len-1] != '\0') {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MNL_TYPE_STRING:
|
||||||
|
if (attr_len == 0) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MNL_TYPE_NESTED:
|
||||||
|
/* empty nested attributes are OK. */
|
||||||
|
if (attr_len == 0)
|
||||||
|
break;
|
||||||
|
/* if not empty, they must contain one header, eg. flag */
|
||||||
|
if (attr_len < MNL_ATTR_HDRLEN) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* make gcc happy. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (exp_len && attr_len > exp_len) {
|
||||||
|
errno = ERANGE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {
|
||||||
|
[MNL_TYPE_U8] = sizeof(uint8_t),
|
||||||
|
[MNL_TYPE_U16] = sizeof(uint16_t),
|
||||||
|
[MNL_TYPE_U32] = sizeof(uint32_t),
|
||||||
|
[MNL_TYPE_U64] = sizeof(uint64_t),
|
||||||
|
[MNL_TYPE_MSECS] = sizeof(uint64_t),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_validate - validate netlink attribute (simplified version)
|
||||||
|
* \param attr pointer to netlink attribute that we want to validate
|
||||||
|
* \param type data type (see enum mnl_attr_data_type)
|
||||||
|
*
|
||||||
|
* The validation is based on the data type. Specifically, it checks that
|
||||||
|
* integers (u8, u16, u32 and u64) have enough room for them. This function
|
||||||
|
* returns -1 in case of error, and errno is explicitly set.
|
||||||
|
*/
|
||||||
|
int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)
|
||||||
|
{
|
||||||
|
int exp_len;
|
||||||
|
|
||||||
|
if (type >= MNL_TYPE_MAX) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
exp_len = mnl_attr_data_type_len[type];
|
||||||
|
return __mnl_attr_validate(attr, type, exp_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_validate2 - validate netlink attribute (extended version)
|
||||||
|
* \param attr pointer to netlink attribute that we want to validate
|
||||||
|
* \param type attribute type (see enum mnl_attr_data_type)
|
||||||
|
* \param exp_len expected attribute data size
|
||||||
|
*
|
||||||
|
* This function allows to perform a more accurate validation for attributes
|
||||||
|
* whose size is variable. If the size of the attribute is not what we expect,
|
||||||
|
* this functions returns -1 and errno is explicitly set.
|
||||||
|
*/
|
||||||
|
int mnl_attr_validate2(const struct nlattr *attr,
|
||||||
|
enum mnl_attr_data_type type,
|
||||||
|
size_t exp_len)
|
||||||
|
{
|
||||||
|
if (type >= MNL_TYPE_MAX) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return __mnl_attr_validate(attr, type, exp_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_parse - parse attributes
|
||||||
|
* \param nlh pointer to netlink message
|
||||||
|
* \param offset offset to start parsing from (if payload is after any header)
|
||||||
|
* \param cb callback function that is called for each attribute
|
||||||
|
* \param data pointer to data that is passed to the callback function
|
||||||
|
*
|
||||||
|
* This function allows to iterate over the sequence of attributes that compose
|
||||||
|
* the Netlink message. You can then put the attribute in an array as it
|
||||||
|
* usually happens at this stage or you can use any other data structure (such
|
||||||
|
* as lists or trees).
|
||||||
|
*
|
||||||
|
* This function propagates the return value of the callback, which can be
|
||||||
|
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
|
||||||
|
*/
|
||||||
|
int mnl_attr_parse(const struct nlmsghdr *nlh,
|
||||||
|
unsigned int offset, mnl_attr_cb_t cb,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
int ret = MNL_CB_OK;
|
||||||
|
const struct nlattr *attr;
|
||||||
|
|
||||||
|
mnl_attr_for_each(attr, nlh, offset)
|
||||||
|
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
|
||||||
|
return ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_parse_nested - parse attributes inside a nest
|
||||||
|
* \param nested pointer to netlink attribute that contains a nest
|
||||||
|
* \param cb callback function that is called for each attribute in the nest
|
||||||
|
* \param data pointer to data passed to the callback function
|
||||||
|
*
|
||||||
|
* This function allows to iterate over the sequence of attributes that compose
|
||||||
|
* the Netlink message. You can then put the attribute in an array as it
|
||||||
|
* usually happens at this stage or you can use any other data structure (such
|
||||||
|
* as lists or trees).
|
||||||
|
*
|
||||||
|
* This function propagates the return value of the callback, which can be
|
||||||
|
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
|
||||||
|
*/
|
||||||
|
int mnl_attr_parse_nested(const struct nlattr *nested,
|
||||||
|
mnl_attr_cb_t cb, void *data)
|
||||||
|
{
|
||||||
|
int ret = MNL_CB_OK;
|
||||||
|
const struct nlattr *attr;
|
||||||
|
|
||||||
|
mnl_attr_for_each_nested(attr, nested)
|
||||||
|
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
|
||||||
|
return ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_parse_payload - parse attributes in payload of Netlink message
|
||||||
|
* \param payload pointer to payload of the Netlink message
|
||||||
|
* \param payload_len payload length that contains the attributes
|
||||||
|
* \param cb callback function that is called for each attribute
|
||||||
|
* \param data pointer to data that is passed to the callback function
|
||||||
|
*
|
||||||
|
* This function takes a pointer to the area that contains the attributes,
|
||||||
|
* commonly known as the payload of the Netlink message. Thus, you have to
|
||||||
|
* pass a pointer to the Netlink message payload, instead of the entire
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* This function allows you to iterate over the sequence of attributes that are
|
||||||
|
* located at some payload offset. You can then put the attributes in one array
|
||||||
|
* as usual, or you can use any other data structure (such as lists or trees).
|
||||||
|
*
|
||||||
|
* This function propagates the return value of the callback, which can be
|
||||||
|
* MNL_CB_ERROR, MNL_CB_OK or MNL_CB_STOP.
|
||||||
|
*/
|
||||||
|
int mnl_attr_parse_payload(const void *payload,
|
||||||
|
size_t payload_len,
|
||||||
|
mnl_attr_cb_t cb, void *data)
|
||||||
|
{
|
||||||
|
int ret = MNL_CB_OK;
|
||||||
|
const struct nlattr *attr;
|
||||||
|
|
||||||
|
mnl_attr_for_each_payload(payload, payload_len)
|
||||||
|
if ((ret = cb(attr, data)) <= MNL_CB_STOP)
|
||||||
|
return ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_u8 - returns 8-bit unsigned integer attribute payload
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the 8-bit value of the attribute payload.
|
||||||
|
*/
|
||||||
|
uint8_t mnl_attr_get_u8(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return *((uint8_t *)mnl_attr_get_payload(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_u16 - returns 16-bit unsigned integer attribute payload
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the 16-bit value of the attribute payload.
|
||||||
|
*/
|
||||||
|
uint16_t mnl_attr_get_u16(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return *((uint16_t *)mnl_attr_get_payload(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_u32 - returns 32-bit unsigned integer attribute payload
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the 32-bit value of the attribute payload.
|
||||||
|
*/
|
||||||
|
uint32_t mnl_attr_get_u32(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return *((uint32_t *)mnl_attr_get_payload(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_u64 - returns 64-bit unsigned integer attribute.
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the 64-bit value of the attribute payload. This
|
||||||
|
* function is align-safe, since accessing 64-bit Netlink attributes is a
|
||||||
|
* common source of alignment issues.
|
||||||
|
*/
|
||||||
|
uint64_t mnl_attr_get_u64(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
memcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_get_str - returns pointer to string attribute.
|
||||||
|
* \param attr pointer to netlink attribute
|
||||||
|
*
|
||||||
|
* This function returns the payload of string attribute value.
|
||||||
|
*/
|
||||||
|
const char *mnl_attr_get_str(const struct nlattr *attr)
|
||||||
|
{
|
||||||
|
return mnl_attr_get_payload(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put - add an attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type that you want to add
|
||||||
|
* \param len netlink attribute payload length
|
||||||
|
* \param data pointer to the data that will be stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
size_t len, const void *data)
|
||||||
|
{
|
||||||
|
struct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);
|
||||||
|
uint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;
|
||||||
|
int pad;
|
||||||
|
|
||||||
|
attr->nla_type = type;
|
||||||
|
attr->nla_len = payload_len;
|
||||||
|
memcpy(mnl_attr_get_payload(attr), data, len);
|
||||||
|
pad = MNL_ALIGN(len) - len;
|
||||||
|
if (pad > 0)
|
||||||
|
memset(mnl_attr_get_payload(attr) + len, 0, pad);
|
||||||
|
|
||||||
|
nlh->nlmsg_len += MNL_ALIGN(payload_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u8 - add 8-bit unsigned integer attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 8-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
uint8_t data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, sizeof(uint8_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u16 - add 16-bit unsigned integer attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 16-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
uint16_t data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, sizeof(uint16_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u32 - add 32-bit unsigned integer attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 32-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
uint32_t data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, sizeof(uint32_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u64 - add 64-bit unsigned integer attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 64-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
uint64_t data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, sizeof(uint64_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_str - add string attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data pointer to string data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
const char *data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, strlen(data), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_strz - add string attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data pointer to string data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function is similar to mnl_attr_put_str, but it includes the
|
||||||
|
* NUL/zero ('\0') terminator at the end of the string.
|
||||||
|
*
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type,
|
||||||
|
const char *data)
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, type, strlen(data)+1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_nest_start - start an attribute nest
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
*
|
||||||
|
* This function adds the attribute header that identifies the beginning of
|
||||||
|
* an attribute nest. This function always returns a valid pointer to the
|
||||||
|
* beginning of the nest.
|
||||||
|
*/
|
||||||
|
struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh,
|
||||||
|
uint16_t type)
|
||||||
|
{
|
||||||
|
struct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);
|
||||||
|
|
||||||
|
/* set start->nla_len in mnl_attr_nest_end() */
|
||||||
|
start->nla_type = NLA_F_NESTED | type;
|
||||||
|
nlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_check - add an attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type that you want to add
|
||||||
|
* \param len netlink attribute payload length
|
||||||
|
* \param data pointer to the data that will be stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, size_t len,
|
||||||
|
const void *data)
|
||||||
|
{
|
||||||
|
if (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)
|
||||||
|
return false;
|
||||||
|
mnl_attr_put(nlh, type, len, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u8_check - add 8-bit unsigned int attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 8-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, uint8_t data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u16_check - add 16-bit unsigned int attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 16-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, uint16_t data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u32_check - add 32-bit unsigned int attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 32-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, uint32_t data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_u64_check - add 64-bit unsigned int attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data 64-bit unsigned integer data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, uint64_t data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, sizeof(uint64_t), &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_str_check - add string attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data pointer to string data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
* This function updates the length field of the Netlink message (nlmsg_len)
|
||||||
|
* by adding the size (header + payload) of the new attribute.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, const char *data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, strlen(data), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_put_strz_check - add string attribute to netlink message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
* \param data pointer to string data that is stored by the new attribute
|
||||||
|
*
|
||||||
|
* This function is similar to mnl_attr_put_str, but it includes the
|
||||||
|
* NUL/zero ('\0') terminator at the end of the string.
|
||||||
|
*
|
||||||
|
* This function first checks that the data can be added to the message
|
||||||
|
* (fits into the buffer) and then updates the length field of the Netlink
|
||||||
|
* message (nlmsg_len) by adding the size (header + payload) of the new
|
||||||
|
* attribute. The function returns true if the attribute could be added
|
||||||
|
* to the message, otherwise false is returned.
|
||||||
|
*/
|
||||||
|
bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen,
|
||||||
|
uint16_t type, const char *data)
|
||||||
|
{
|
||||||
|
return mnl_attr_put_check(nlh, buflen, type, strlen(data)+1, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_nest_start_check - start an attribute nest
|
||||||
|
* \param buflen size of buffer which stores the message
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param type netlink attribute type
|
||||||
|
*
|
||||||
|
* This function adds the attribute header that identifies the beginning of
|
||||||
|
* an attribute nest. If the nested attribute cannot be added then NULL,
|
||||||
|
* otherwise valid pointer to the beginning of the nest is returned.
|
||||||
|
*/
|
||||||
|
struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh,
|
||||||
|
size_t buflen,
|
||||||
|
uint16_t type)
|
||||||
|
{
|
||||||
|
if (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)
|
||||||
|
return NULL;
|
||||||
|
return mnl_attr_nest_start(nlh, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_nest_end - end an attribute nest
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param start pointer to the attribute nest returned by mnl_attr_nest_start()
|
||||||
|
*
|
||||||
|
* This function updates the attribute header that identifies the nest.
|
||||||
|
*/
|
||||||
|
void mnl_attr_nest_end(struct nlmsghdr *nlh,
|
||||||
|
struct nlattr *start)
|
||||||
|
{
|
||||||
|
start->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_attr_nest_cancel - cancel an attribute nest
|
||||||
|
* \param nlh pointer to the netlink message
|
||||||
|
* \param start pointer to the attribute nest returned by mnl_attr_nest_start()
|
||||||
|
*
|
||||||
|
* This function updates the attribute header that identifies the nest.
|
||||||
|
*/
|
||||||
|
void mnl_attr_nest_cancel(struct nlmsghdr *nlh,
|
||||||
|
struct nlattr *start)
|
||||||
|
{
|
||||||
|
nlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
167
package/wwan/app/fibocom-dial/src/libmnl/callback.c
Executable file
167
package/wwan/app/fibocom-dial/src/libmnl/callback.c
Executable file
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "libmnl.h"
|
||||||
|
|
||||||
|
static int mnl_cb_noop(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mnl_cb_error(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
|
||||||
|
|
||||||
|
if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {
|
||||||
|
errno = EBADMSG;
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
/* Netlink subsystems returns the errno value with different signess */
|
||||||
|
if (err->error < 0)
|
||||||
|
errno = -err->error;
|
||||||
|
else
|
||||||
|
errno = err->error;
|
||||||
|
|
||||||
|
return err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mnl_cb_stop(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
return MNL_CB_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {
|
||||||
|
[NLMSG_NOOP] = mnl_cb_noop,
|
||||||
|
[NLMSG_ERROR] = mnl_cb_error,
|
||||||
|
[NLMSG_DONE] = mnl_cb_stop,
|
||||||
|
[NLMSG_OVERRUN] = mnl_cb_noop,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int __mnl_cb_run(const void *buf, size_t numbytes,
|
||||||
|
unsigned int seq, unsigned int portid,
|
||||||
|
mnl_cb_t cb_data, void *data,
|
||||||
|
const mnl_cb_t *cb_ctl_array,
|
||||||
|
unsigned int cb_ctl_array_len)
|
||||||
|
{
|
||||||
|
int ret = MNL_CB_OK, len = numbytes;
|
||||||
|
const struct nlmsghdr *nlh = buf;
|
||||||
|
|
||||||
|
while (mnl_nlmsg_ok(nlh, len)) {
|
||||||
|
/* check message source */
|
||||||
|
if (!mnl_nlmsg_portid_ok(nlh, portid)) {
|
||||||
|
errno = ESRCH;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* perform sequence tracking */
|
||||||
|
if (!mnl_nlmsg_seq_ok(nlh, seq)) {
|
||||||
|
errno = EPROTO;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dump was interrupted */
|
||||||
|
if (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {
|
||||||
|
errno = EINTR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* netlink data message handling */
|
||||||
|
if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {
|
||||||
|
if (cb_data){
|
||||||
|
ret = cb_data(nlh, data);
|
||||||
|
if (ret <= MNL_CB_STOP)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (nlh->nlmsg_type < cb_ctl_array_len) {
|
||||||
|
if (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {
|
||||||
|
ret = cb_ctl_array[nlh->nlmsg_type](nlh, data);
|
||||||
|
if (ret <= MNL_CB_STOP)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (default_cb_array[nlh->nlmsg_type]) {
|
||||||
|
ret = default_cb_array[nlh->nlmsg_type](nlh, data);
|
||||||
|
if (ret <= MNL_CB_STOP)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
nlh = mnl_nlmsg_next(nlh, &len);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup callback Callback helpers
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_cb_run2 - callback runqueue for netlink messages
|
||||||
|
* \param buf buffer that contains the netlink messages
|
||||||
|
* \param numbytes number of bytes stored in the buffer
|
||||||
|
* \param seq sequence number that we expect to receive
|
||||||
|
* \param portid Netlink PortID that we expect to receive
|
||||||
|
* \param cb_data callback handler for data messages
|
||||||
|
* \param data pointer to data that will be passed to the data callback handler
|
||||||
|
* \param cb_ctl_array array of custom callback handlers from control messages
|
||||||
|
* \param cb_ctl_array_len array length of custom control callback handlers
|
||||||
|
*
|
||||||
|
* You can set the cb_ctl_array to NULL if you want to use the default control
|
||||||
|
* callback handlers, in that case, the parameter cb_ctl_array_len is not
|
||||||
|
* checked.
|
||||||
|
*
|
||||||
|
* Your callback may return three possible values:
|
||||||
|
* - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
|
||||||
|
* - MNL_CB_STOP (=0): stop callback runqueue.
|
||||||
|
* - MNL_CB_OK (>=1): no problem has occurred.
|
||||||
|
*
|
||||||
|
* This function propagates the callback return value. On error, it returns
|
||||||
|
* -1 and errno is explicitly set. If the portID is not the expected, errno
|
||||||
|
* is set to ESRCH. If the sequence number is not the expected, errno is set
|
||||||
|
* to EPROTO. If the dump was interrupted, errno is set to EINTR and you should
|
||||||
|
* request a new fresh dump again.
|
||||||
|
*/
|
||||||
|
int mnl_cb_run2(const void *buf, size_t numbytes,
|
||||||
|
unsigned int seq, unsigned int portid,
|
||||||
|
mnl_cb_t cb_data, void *data,
|
||||||
|
const mnl_cb_t *cb_ctl_array,
|
||||||
|
unsigned int cb_ctl_array_len)
|
||||||
|
{
|
||||||
|
return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,
|
||||||
|
cb_ctl_array, cb_ctl_array_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_cb_run - callback runqueue for netlink messages (simplified version)
|
||||||
|
* \param buf buffer that contains the netlink messages
|
||||||
|
* \param numbytes number of bytes stored in the buffer
|
||||||
|
* \param seq sequence number that we expect to receive
|
||||||
|
* \param portid Netlink PortID that we expect to receive
|
||||||
|
* \param cb_data callback handler for data messages
|
||||||
|
* \param data pointer to data that will be passed to the data callback handler
|
||||||
|
*
|
||||||
|
* This function is like mnl_cb_run2() but it does not allow you to set
|
||||||
|
* the control callback handlers.
|
||||||
|
*
|
||||||
|
* Your callback may return three possible values:
|
||||||
|
* - MNL_CB_ERROR (<=-1): an error has occurred. Stop callback runqueue.
|
||||||
|
* - MNL_CB_STOP (=0): stop callback runqueue.
|
||||||
|
* - MNL_CB_OK (>=1): no problems has occurred.
|
||||||
|
*
|
||||||
|
* This function propagates the callback return value.
|
||||||
|
*/
|
||||||
|
int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
|
||||||
|
unsigned int portid, mnl_cb_t cb_data, void *data)
|
||||||
|
{
|
||||||
|
return __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
5
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcp.h
Executable file
5
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcp.h
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#ifndef __DHCP_H__
|
||||||
|
#define __DHCP_H__
|
||||||
|
|
||||||
|
int do_dhcp(char *iname);
|
||||||
|
#endif //__DHCP_H__
|
515
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpclient.c
Executable file
515
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpclient.c
Executable file
@ -0,0 +1,515 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include "../ifutils.h"
|
||||||
|
#include "dhcpmsg.h"
|
||||||
|
#include "packet.h"
|
||||||
|
|
||||||
|
#define VERBOSE 2
|
||||||
|
|
||||||
|
static int verbose = 1;
|
||||||
|
static char errmsg[2048];
|
||||||
|
|
||||||
|
typedef unsigned long long msecs_t;
|
||||||
|
#if VERBOSE
|
||||||
|
void dump_dhcp_msg();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
msecs_t get_msecs(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
|
||||||
|
(((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void printerr(char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
printf("%s\n", errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *dhcp_lasterror()
|
||||||
|
{
|
||||||
|
return errmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fatal(const char *reason)
|
||||||
|
{
|
||||||
|
printerr("%s: %s\n", reason, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
// exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct dhcp_info dhcp_info;
|
||||||
|
|
||||||
|
struct dhcp_info {
|
||||||
|
uint32_t type;
|
||||||
|
|
||||||
|
uint32_t ipaddr;
|
||||||
|
uint32_t gateway;
|
||||||
|
uint32_t prefixLength;
|
||||||
|
|
||||||
|
uint32_t dns1;
|
||||||
|
uint32_t dns2;
|
||||||
|
|
||||||
|
uint32_t serveraddr;
|
||||||
|
uint32_t lease;
|
||||||
|
};
|
||||||
|
|
||||||
|
dhcp_info last_good_info;
|
||||||
|
|
||||||
|
void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
|
||||||
|
uint32_t *dns1, uint32_t *dns2, uint32_t *server,
|
||||||
|
uint32_t *lease)
|
||||||
|
{
|
||||||
|
*ipaddr = last_good_info.ipaddr;
|
||||||
|
*gateway = last_good_info.gateway;
|
||||||
|
*prefixLength = last_good_info.prefixLength;
|
||||||
|
*dns1 = last_good_info.dns1;
|
||||||
|
*dns2 = last_good_info.dns2;
|
||||||
|
*server = last_good_info.serveraddr;
|
||||||
|
*lease = last_good_info.lease;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dhcp_configure(const char *ifname, dhcp_info *info)
|
||||||
|
{
|
||||||
|
last_good_info = *info;
|
||||||
|
return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway,
|
||||||
|
info->dns1, info->dns2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *dhcp_type_to_name(uint32_t type)
|
||||||
|
{
|
||||||
|
switch(type) {
|
||||||
|
case DHCPDISCOVER: return "discover";
|
||||||
|
case DHCPOFFER: return "offer";
|
||||||
|
case DHCPREQUEST: return "request";
|
||||||
|
case DHCPDECLINE: return "decline";
|
||||||
|
case DHCPACK: return "ack";
|
||||||
|
case DHCPNAK: return "nak";
|
||||||
|
case DHCPRELEASE: return "release";
|
||||||
|
case DHCPINFORM: return "inform";
|
||||||
|
default: return "???";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_dhcp_info(dhcp_info *info)
|
||||||
|
{
|
||||||
|
char addr[20], gway[20];
|
||||||
|
printf("--- dhcp %s (%d) ---\n",
|
||||||
|
dhcp_type_to_name(info->type), info->type);
|
||||||
|
strcpy(addr, ipaddr_to_string_v4(info->ipaddr));
|
||||||
|
strcpy(gway, ipaddr_to_string_v4(info->gateway));
|
||||||
|
printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
|
||||||
|
if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1));
|
||||||
|
if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2));
|
||||||
|
printf("server %s, lease %d seconds\n",
|
||||||
|
ipaddr_to_string_v4(info->serveraddr), info->lease);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
|
||||||
|
{
|
||||||
|
uint8_t *x;
|
||||||
|
unsigned int opt;
|
||||||
|
int optlen;
|
||||||
|
|
||||||
|
memset(info, 0, sizeof(dhcp_info));
|
||||||
|
if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
|
||||||
|
|
||||||
|
len -= (DHCP_MSG_FIXED_SIZE + 4);
|
||||||
|
|
||||||
|
if (msg->options[0] != OPT_COOKIE1) return -1;
|
||||||
|
if (msg->options[1] != OPT_COOKIE2) return -1;
|
||||||
|
if (msg->options[2] != OPT_COOKIE3) return -1;
|
||||||
|
if (msg->options[3] != OPT_COOKIE4) return -1;
|
||||||
|
|
||||||
|
x = msg->options + 4;
|
||||||
|
|
||||||
|
while (len > 2) {
|
||||||
|
opt = *x++;
|
||||||
|
if (opt == OPT_PAD) {
|
||||||
|
len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (opt == OPT_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
optlen = *x++;
|
||||||
|
len -= 2;
|
||||||
|
if (optlen > len) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch(opt) {
|
||||||
|
case OPT_SUBNET_MASK:
|
||||||
|
if (optlen >= 4) {
|
||||||
|
in_addr_t mask;
|
||||||
|
memcpy(&mask, x, 4);
|
||||||
|
info->prefixLength = mask_to_prefix_v4(mask);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OPT_GATEWAY:
|
||||||
|
if (optlen >= 4) memcpy(&info->gateway, x, 4);
|
||||||
|
break;
|
||||||
|
case OPT_DNS:
|
||||||
|
if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
|
||||||
|
if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
|
||||||
|
break;
|
||||||
|
case OPT_LEASE_TIME:
|
||||||
|
if (optlen >= 4) {
|
||||||
|
memcpy(&info->lease, x, 4);
|
||||||
|
info->lease = ntohl(info->lease);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OPT_SERVER_ID:
|
||||||
|
if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
|
||||||
|
break;
|
||||||
|
case OPT_MESSAGE_TYPE:
|
||||||
|
info->type = *x;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
x += optlen;
|
||||||
|
len -= optlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
info->ipaddr = msg->yiaddr;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if VERBOSE
|
||||||
|
|
||||||
|
static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char *cp = buf;
|
||||||
|
char *buf_end = buf + buf_size;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_dhcp_msg(dhcp_msg *msg, int len)
|
||||||
|
{
|
||||||
|
unsigned char *x;
|
||||||
|
unsigned int n,c;
|
||||||
|
int optsz;
|
||||||
|
const char *name;
|
||||||
|
char buf[2048];
|
||||||
|
|
||||||
|
if (len < DHCP_MSG_FIXED_SIZE) {
|
||||||
|
printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len -= DHCP_MSG_FIXED_SIZE;
|
||||||
|
|
||||||
|
if (msg->op == OP_BOOTREQUEST)
|
||||||
|
name = "BOOTREQUEST";
|
||||||
|
else if (msg->op == OP_BOOTREPLY)
|
||||||
|
name = "BOOTREPLY";
|
||||||
|
else
|
||||||
|
name = "????";
|
||||||
|
|
||||||
|
c = msg->hlen > 16 ? 16 : msg->hlen;
|
||||||
|
hex2str(buf, sizeof(buf), msg->chaddr, c);
|
||||||
|
|
||||||
|
for (n = 0; n < 64; n++) {
|
||||||
|
unsigned char x = msg->sname[n];
|
||||||
|
if ((x < ' ') || (x > 127)) {
|
||||||
|
if (x == 0) break;
|
||||||
|
msg->sname[n] = '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg->sname[63] = 0;
|
||||||
|
|
||||||
|
for (n = 0; n < 128; n++) {
|
||||||
|
unsigned char x = msg->file[n];
|
||||||
|
if ((x < ' ') || (x > 127)) {
|
||||||
|
if (x == 0) break;
|
||||||
|
msg->file[n] = '.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg->file[127] = 0;
|
||||||
|
|
||||||
|
if (len < 4) return;
|
||||||
|
len -= 4;
|
||||||
|
x = msg->options + 4;
|
||||||
|
|
||||||
|
while (len > 2) {
|
||||||
|
if (*x == 0) {
|
||||||
|
x++;
|
||||||
|
len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (*x == OPT_END) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len -= 2;
|
||||||
|
optsz = x[1];
|
||||||
|
if (optsz > len) break;
|
||||||
|
if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
|
||||||
|
if ((unsigned int)optsz < sizeof(buf) - 1) {
|
||||||
|
n = optsz;
|
||||||
|
} else {
|
||||||
|
n = sizeof(buf) - 1;
|
||||||
|
}
|
||||||
|
memcpy(buf, &x[2], n);
|
||||||
|
buf[n] = '\0';
|
||||||
|
} else {
|
||||||
|
hex2str(buf, sizeof(buf), &x[2], optsz);
|
||||||
|
}
|
||||||
|
if (x[0] == OPT_MESSAGE_TYPE)
|
||||||
|
name = dhcp_type_to_name(x[2]);
|
||||||
|
else
|
||||||
|
name = NULL;
|
||||||
|
len -= optsz;
|
||||||
|
x = x + optsz + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
|
||||||
|
{
|
||||||
|
#if VERBOSE > 1
|
||||||
|
dump_dhcp_msg(msg, size);
|
||||||
|
#endif
|
||||||
|
return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
|
||||||
|
PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
|
||||||
|
{
|
||||||
|
if (sz < DHCP_MSG_FIXED_SIZE) {
|
||||||
|
if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (reply->op != OP_BOOTREPLY) {
|
||||||
|
if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (reply->xid != msg->xid) {
|
||||||
|
if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
|
||||||
|
ntohl(msg->xid));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (reply->htype != msg->htype) {
|
||||||
|
if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (reply->hlen != msg->hlen) {
|
||||||
|
if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
|
||||||
|
if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define STATE_SELECTING 1
|
||||||
|
#define STATE_REQUESTING 2
|
||||||
|
|
||||||
|
#define TIMEOUT_INITIAL 4000
|
||||||
|
#define TIMEOUT_MAX 32000
|
||||||
|
|
||||||
|
int dhcp_init_ifc(const char *ifname)
|
||||||
|
{
|
||||||
|
dhcp_msg discover_msg;
|
||||||
|
dhcp_msg request_msg;
|
||||||
|
dhcp_msg reply;
|
||||||
|
dhcp_msg *msg;
|
||||||
|
dhcp_info info;
|
||||||
|
int s, r, size;
|
||||||
|
int valid_reply;
|
||||||
|
uint32_t xid;
|
||||||
|
unsigned char hwaddr[6];
|
||||||
|
struct pollfd pfd;
|
||||||
|
unsigned int state;
|
||||||
|
unsigned int timeout;
|
||||||
|
int if_index;
|
||||||
|
|
||||||
|
xid = (uint32_t) get_msecs();
|
||||||
|
|
||||||
|
if (if_get_hwaddr(ifname, hwaddr)) {
|
||||||
|
return fatal("cannot obtain interface address");
|
||||||
|
}
|
||||||
|
if ((if_index = if_nametoindex(ifname)) == 0) {
|
||||||
|
return fatal("cannot obtain interface index");
|
||||||
|
}
|
||||||
|
|
||||||
|
s = open_raw_socket(ifname, hwaddr, if_index);
|
||||||
|
|
||||||
|
timeout = TIMEOUT_INITIAL;
|
||||||
|
state = STATE_SELECTING;
|
||||||
|
info.type = 0;
|
||||||
|
goto transmit;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
pfd.fd = s;
|
||||||
|
pfd.events = POLLIN;
|
||||||
|
pfd.revents = 0;
|
||||||
|
r = poll(&pfd, 1, timeout);
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
#if VERBOSE
|
||||||
|
printerr("TIMEOUT\n");
|
||||||
|
#endif
|
||||||
|
if (timeout >= TIMEOUT_MAX) {
|
||||||
|
printerr("timed out\n");
|
||||||
|
if ( info.type == DHCPOFFER ) {
|
||||||
|
printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
|
||||||
|
return dhcp_configure(ifname, &info);
|
||||||
|
}
|
||||||
|
errno = ETIME;
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
timeout = timeout * 2;
|
||||||
|
|
||||||
|
transmit:
|
||||||
|
size = 0;
|
||||||
|
msg = NULL;
|
||||||
|
switch(state) {
|
||||||
|
case STATE_SELECTING:
|
||||||
|
msg = &discover_msg;
|
||||||
|
size = init_dhcp_discover_msg(msg, hwaddr, xid);
|
||||||
|
break;
|
||||||
|
case STATE_REQUESTING:
|
||||||
|
msg = &request_msg;
|
||||||
|
size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = 0;
|
||||||
|
}
|
||||||
|
if (size != 0) {
|
||||||
|
r = send_message(s, if_index, msg, size);
|
||||||
|
if (r < 0) {
|
||||||
|
printerr("error sending dhcp msg: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
if ((errno == EAGAIN) || (errno == EINTR)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return fatal("poll failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
r = receive_packet(s, &reply);
|
||||||
|
if (r < 0) {
|
||||||
|
if (errno != 0) {
|
||||||
|
printf("receive_packet failed (%d): %s", r, strerror(errno));
|
||||||
|
if (errno == ENETDOWN || errno == ENXIO) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if VERBOSE > 1
|
||||||
|
dump_dhcp_msg(&reply, r);
|
||||||
|
#endif
|
||||||
|
decode_dhcp_msg(&reply, r, &info);
|
||||||
|
|
||||||
|
if (state == STATE_SELECTING) {
|
||||||
|
valid_reply = is_valid_reply(&discover_msg, &reply, r);
|
||||||
|
} else {
|
||||||
|
valid_reply = is_valid_reply(&request_msg, &reply, r);
|
||||||
|
}
|
||||||
|
if (!valid_reply) {
|
||||||
|
printerr("invalid reply\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose) dump_dhcp_info(&info);
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case STATE_SELECTING:
|
||||||
|
if (info.type == DHCPOFFER) {
|
||||||
|
state = STATE_REQUESTING;
|
||||||
|
timeout = TIMEOUT_INITIAL;
|
||||||
|
xid++;
|
||||||
|
goto transmit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STATE_REQUESTING:
|
||||||
|
if (info.type == DHCPACK) {
|
||||||
|
printerr("configuring %s\n", ifname);
|
||||||
|
close(s);
|
||||||
|
return dhcp_configure(ifname, &info);
|
||||||
|
} else if (info.type == DHCPNAK) {
|
||||||
|
printerr("configuration request denied\n");
|
||||||
|
close(s);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
printerr("ignoring %s message in state %d\n",
|
||||||
|
dhcp_type_to_name(info.type), state);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int do_dhcp(char *iname)
|
||||||
|
{
|
||||||
|
if (if_set_addr_v4(iname, 0, 32)) {
|
||||||
|
printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (if_link_up(iname)) {
|
||||||
|
printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dhcp_init_ifc(iname);
|
||||||
|
}
|
100
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpmsg.c
Executable file
100
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpmsg.c
Executable file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#include "dhcpmsg.h"
|
||||||
|
|
||||||
|
static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
|
||||||
|
{
|
||||||
|
uint8_t *x;
|
||||||
|
|
||||||
|
memset(msg, 0, sizeof(dhcp_msg));
|
||||||
|
|
||||||
|
msg->op = OP_BOOTREQUEST;
|
||||||
|
msg->htype = HTYPE_ETHER;
|
||||||
|
msg->hlen = 6;
|
||||||
|
msg->hops = 0;
|
||||||
|
|
||||||
|
msg->flags = htons(FLAGS_BROADCAST);
|
||||||
|
|
||||||
|
msg->xid = xid;
|
||||||
|
|
||||||
|
memcpy(msg->chaddr, hwaddr, 6);
|
||||||
|
|
||||||
|
x = msg->options;
|
||||||
|
|
||||||
|
*x++ = OPT_COOKIE1;
|
||||||
|
*x++ = OPT_COOKIE2;
|
||||||
|
*x++ = OPT_COOKIE3;
|
||||||
|
*x++ = OPT_COOKIE4;
|
||||||
|
|
||||||
|
*x++ = OPT_MESSAGE_TYPE;
|
||||||
|
*x++ = 1;
|
||||||
|
*x++ = type;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
|
||||||
|
{
|
||||||
|
uint8_t *x;
|
||||||
|
|
||||||
|
x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
|
||||||
|
|
||||||
|
*x++ = OPT_PARAMETER_LIST;
|
||||||
|
*x++ = 4;
|
||||||
|
*x++ = OPT_SUBNET_MASK;
|
||||||
|
*x++ = OPT_GATEWAY;
|
||||||
|
*x++ = OPT_DNS;
|
||||||
|
*x++ = OPT_BROADCAST_ADDR;
|
||||||
|
|
||||||
|
*x++ = OPT_END;
|
||||||
|
|
||||||
|
return DHCP_MSG_FIXED_SIZE + (x - msg->options);
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
|
||||||
|
uint32_t ipaddr, uint32_t serveraddr)
|
||||||
|
{
|
||||||
|
uint8_t *x;
|
||||||
|
|
||||||
|
x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
|
||||||
|
|
||||||
|
*x++ = OPT_PARAMETER_LIST;
|
||||||
|
*x++ = 4;
|
||||||
|
*x++ = OPT_SUBNET_MASK;
|
||||||
|
*x++ = OPT_GATEWAY;
|
||||||
|
*x++ = OPT_DNS;
|
||||||
|
*x++ = OPT_BROADCAST_ADDR;
|
||||||
|
|
||||||
|
*x++ = OPT_REQUESTED_IP;
|
||||||
|
*x++ = 4;
|
||||||
|
memcpy(x, &ipaddr, 4);
|
||||||
|
x += 4;
|
||||||
|
|
||||||
|
*x++ = OPT_SERVER_ID;
|
||||||
|
*x++ = 4;
|
||||||
|
memcpy(x, &serveraddr, 4);
|
||||||
|
x += 4;
|
||||||
|
|
||||||
|
*x++ = OPT_END;
|
||||||
|
|
||||||
|
return DHCP_MSG_FIXED_SIZE + (x - msg->options);
|
||||||
|
}
|
106
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpmsg.h
Executable file
106
package/wwan/app/fibocom-dial/src/libmnl/dhcp/dhcpmsg.h
Executable file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WIFI_DHCP_H_
|
||||||
|
#define _WIFI_DHCP_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PORT_BOOTP_SERVER 67
|
||||||
|
#define PORT_BOOTP_CLIENT 68
|
||||||
|
|
||||||
|
/* RFC 2131 p 9 */
|
||||||
|
typedef struct dhcp_msg dhcp_msg;
|
||||||
|
|
||||||
|
#define OP_BOOTREQUEST 1
|
||||||
|
#define OP_BOOTREPLY 2
|
||||||
|
|
||||||
|
#define FLAGS_BROADCAST 0x8000
|
||||||
|
|
||||||
|
#define HTYPE_ETHER 1
|
||||||
|
|
||||||
|
struct dhcp_msg
|
||||||
|
{
|
||||||
|
uint8_t op; /* BOOTREQUEST / BOOTREPLY */
|
||||||
|
uint8_t htype; /* hw addr type */
|
||||||
|
uint8_t hlen; /* hw addr len */
|
||||||
|
uint8_t hops; /* client set to 0 */
|
||||||
|
|
||||||
|
uint32_t xid; /* transaction id */
|
||||||
|
|
||||||
|
uint16_t secs; /* seconds since start of acq */
|
||||||
|
uint16_t flags;
|
||||||
|
|
||||||
|
uint32_t ciaddr; /* client IP addr */
|
||||||
|
uint32_t yiaddr; /* your (client) IP addr */
|
||||||
|
uint32_t siaddr; /* ip addr of next server */
|
||||||
|
/* (DHCPOFFER and DHCPACK) */
|
||||||
|
uint32_t giaddr; /* relay agent IP addr */
|
||||||
|
|
||||||
|
uint8_t chaddr[16]; /* client hw addr */
|
||||||
|
char sname[64]; /* asciiz server hostname */
|
||||||
|
char file[128]; /* asciiz boot file name */
|
||||||
|
|
||||||
|
uint8_t options[1024]; /* optional parameters */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DHCP_MSG_FIXED_SIZE 236
|
||||||
|
|
||||||
|
/* first four bytes of options are a cookie to indicate that
|
||||||
|
** the payload are DHCP options as opposed to some other BOOTP
|
||||||
|
** extension.
|
||||||
|
*/
|
||||||
|
#define OPT_COOKIE1 0x63
|
||||||
|
#define OPT_COOKIE2 0x82
|
||||||
|
#define OPT_COOKIE3 0x53
|
||||||
|
#define OPT_COOKIE4 0x63
|
||||||
|
|
||||||
|
/* BOOTP/DHCP options - see RFC 2132 */
|
||||||
|
#define OPT_PAD 0
|
||||||
|
|
||||||
|
#define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
|
||||||
|
#define OPT_TIME_OFFSET 2 /* 4 <seconds> */
|
||||||
|
#define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
|
||||||
|
#define OPT_DNS 6 /* 4*n <ipaddr> * n */
|
||||||
|
#define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
|
||||||
|
#define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
|
||||||
|
|
||||||
|
#define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
|
||||||
|
#define OPT_LEASE_TIME 51 /* 4 <seconds> */
|
||||||
|
#define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
|
||||||
|
#define OPT_SERVER_ID 54 /* 4 <ipaddr> */
|
||||||
|
#define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
|
||||||
|
#define OPT_MESSAGE 56 /* n <errorstring> */
|
||||||
|
#define OPT_CLASS_ID 60 /* n <opaque> */
|
||||||
|
#define OPT_CLIENT_ID 61 /* n <opaque> */
|
||||||
|
#define OPT_END 255
|
||||||
|
|
||||||
|
/* DHCP message types */
|
||||||
|
#define DHCPDISCOVER 1
|
||||||
|
#define DHCPOFFER 2
|
||||||
|
#define DHCPREQUEST 3
|
||||||
|
#define DHCPDECLINE 4
|
||||||
|
#define DHCPACK 5
|
||||||
|
#define DHCPNAK 6
|
||||||
|
#define DHCPRELEASE 7
|
||||||
|
#define DHCPINFORM 8
|
||||||
|
|
||||||
|
int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);
|
||||||
|
|
||||||
|
int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
|
||||||
|
uint32_t ipaddr, uint32_t serveraddr);
|
||||||
|
|
||||||
|
#endif
|
247
package/wwan/app/fibocom-dial/src/libmnl/dhcp/packet.c
Executable file
247
package/wwan/app/fibocom-dial/src/libmnl/dhcp/packet.c
Executable file
@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/if_packet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/ip.h>
|
||||||
|
#include <netinet/udp.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "dhcpmsg.h"
|
||||||
|
|
||||||
|
int fatal();
|
||||||
|
|
||||||
|
int open_raw_socket(const char *ifname __attribute__((unused)), uint8_t *hwaddr, int if_index)
|
||||||
|
{
|
||||||
|
int s;
|
||||||
|
struct sockaddr_ll bindaddr;
|
||||||
|
|
||||||
|
if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
|
||||||
|
return fatal("socket(PF_PACKET)");
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&bindaddr, 0, sizeof(bindaddr));
|
||||||
|
bindaddr.sll_family = AF_PACKET;
|
||||||
|
bindaddr.sll_protocol = htons(ETH_P_IP);
|
||||||
|
bindaddr.sll_halen = ETH_ALEN;
|
||||||
|
memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
|
||||||
|
bindaddr.sll_ifindex = if_index;
|
||||||
|
|
||||||
|
if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
|
||||||
|
return fatal("Cannot bind raw socket to interface");
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
|
||||||
|
{
|
||||||
|
uint16_t *up = (uint16_t *)buffer;
|
||||||
|
uint32_t sum = startsum;
|
||||||
|
uint32_t upper16;
|
||||||
|
|
||||||
|
while (count > 1) {
|
||||||
|
sum += *up++;
|
||||||
|
count -= 2;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
sum += (uint16_t) *(uint8_t *)up;
|
||||||
|
}
|
||||||
|
while ((upper16 = (sum >> 16)) != 0) {
|
||||||
|
sum = (sum & 0xffff) + upper16;
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t finish_sum(uint32_t sum)
|
||||||
|
{
|
||||||
|
return ~sum & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
|
||||||
|
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
|
||||||
|
{
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
struct iovec iov[3];
|
||||||
|
uint32_t udpsum;
|
||||||
|
uint16_t temp;
|
||||||
|
struct msghdr msghdr;
|
||||||
|
struct sockaddr_ll destaddr;
|
||||||
|
|
||||||
|
ip.version = IPVERSION;
|
||||||
|
ip.ihl = sizeof(ip) >> 2;
|
||||||
|
ip.tos = 0;
|
||||||
|
ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
|
||||||
|
ip.id = 0;
|
||||||
|
ip.frag_off = 0;
|
||||||
|
ip.ttl = IPDEFTTL;
|
||||||
|
ip.protocol = IPPROTO_UDP;
|
||||||
|
ip.check = 0;
|
||||||
|
ip.saddr = saddr;
|
||||||
|
ip.daddr = daddr;
|
||||||
|
ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
|
||||||
|
|
||||||
|
udp.source = htons(sport);
|
||||||
|
udp.dest = htons(dport);
|
||||||
|
udp.len = htons(sizeof(udp) + size);
|
||||||
|
udp.check = 0;
|
||||||
|
|
||||||
|
/* Calculate checksum for pseudo header */
|
||||||
|
udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
|
||||||
|
udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
|
||||||
|
temp = htons(IPPROTO_UDP);
|
||||||
|
udpsum = checksum(&temp, sizeof(temp), udpsum);
|
||||||
|
temp = udp.len;
|
||||||
|
udpsum = checksum(&temp, sizeof(temp), udpsum);
|
||||||
|
|
||||||
|
/* Add in the checksum for the udp header */
|
||||||
|
udpsum = checksum(&udp, sizeof(udp), udpsum);
|
||||||
|
|
||||||
|
/* Add in the checksum for the data */
|
||||||
|
udpsum = checksum(msg, size, udpsum);
|
||||||
|
udp.check = finish_sum(udpsum);
|
||||||
|
|
||||||
|
iov[0].iov_base = (char *)&ip;
|
||||||
|
iov[0].iov_len = sizeof(ip);
|
||||||
|
iov[1].iov_base = (char *)&udp;
|
||||||
|
iov[1].iov_len = sizeof(udp);
|
||||||
|
iov[2].iov_base = (char *)msg;
|
||||||
|
iov[2].iov_len = size;
|
||||||
|
memset(&destaddr, 0, sizeof(destaddr));
|
||||||
|
destaddr.sll_family = AF_PACKET;
|
||||||
|
destaddr.sll_protocol = htons(ETH_P_IP);
|
||||||
|
destaddr.sll_ifindex = if_index;
|
||||||
|
destaddr.sll_halen = ETH_ALEN;
|
||||||
|
memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
|
||||||
|
|
||||||
|
msghdr.msg_name = &destaddr;
|
||||||
|
msghdr.msg_namelen = sizeof(destaddr);
|
||||||
|
msghdr.msg_iov = iov;
|
||||||
|
msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
|
||||||
|
msghdr.msg_flags = 0;
|
||||||
|
msghdr.msg_control = 0;
|
||||||
|
msghdr.msg_controllen = 0;
|
||||||
|
return sendmsg(s, &msghdr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int receive_packet(int s, struct dhcp_msg *msg)
|
||||||
|
{
|
||||||
|
int nread;
|
||||||
|
int is_valid;
|
||||||
|
struct dhcp_packet {
|
||||||
|
struct iphdr ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
struct dhcp_msg dhcp;
|
||||||
|
} packet;
|
||||||
|
int dhcp_size;
|
||||||
|
uint32_t sum;
|
||||||
|
uint16_t temp;
|
||||||
|
uint32_t saddr, daddr;
|
||||||
|
|
||||||
|
nread = read(s, &packet, sizeof(packet));
|
||||||
|
if (nread < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The raw packet interface gives us all packets received by the
|
||||||
|
* network interface. We need to filter out all packets that are
|
||||||
|
* not meant for us.
|
||||||
|
*/
|
||||||
|
is_valid = 0;
|
||||||
|
if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
|
||||||
|
#if VERBOSE
|
||||||
|
ALOGD("Packet is too small (%d) to be a UDP datagram", nread);
|
||||||
|
#endif
|
||||||
|
} else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
|
||||||
|
#if VERBOSE
|
||||||
|
ALOGD("Not a valid IP packet");
|
||||||
|
#endif
|
||||||
|
} else if (nread < ntohs(packet.ip.tot_len)) {
|
||||||
|
#if VERBOSE
|
||||||
|
ALOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
|
||||||
|
#endif
|
||||||
|
} else if (packet.ip.protocol != IPPROTO_UDP) {
|
||||||
|
#if VERBOSE
|
||||||
|
ALOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
|
||||||
|
#endif
|
||||||
|
} else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
|
||||||
|
#if VERBOSE
|
||||||
|
ALOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
is_valid = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_valid) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seems like it's probably a valid DHCP packet */
|
||||||
|
/* validate IP header checksum */
|
||||||
|
sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
|
||||||
|
if (sum != 0) {
|
||||||
|
printf("IP header checksum failure (0x%x)\n", packet.ip.check);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Validate the UDP checksum.
|
||||||
|
* Since we don't need the IP header anymore, we "borrow" it
|
||||||
|
* to construct the pseudo header used in the checksum calculation.
|
||||||
|
*/
|
||||||
|
dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
|
||||||
|
/*
|
||||||
|
* check validity of dhcp_size.
|
||||||
|
* 1) cannot be negative or zero.
|
||||||
|
* 2) src buffer contains enough bytes to copy
|
||||||
|
* 3) cannot exceed destination buffer
|
||||||
|
*/
|
||||||
|
if ((dhcp_size <= 0) ||
|
||||||
|
((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||
|
||||||
|
((int)sizeof(struct dhcp_msg) < dhcp_size)) {
|
||||||
|
#if VERBOSE
|
||||||
|
printf("Malformed Packet\n");
|
||||||
|
#endif
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
saddr = packet.ip.saddr;
|
||||||
|
daddr = packet.ip.daddr;
|
||||||
|
nread = ntohs(packet.ip.tot_len);
|
||||||
|
memset(&packet.ip, 0, sizeof(packet.ip));
|
||||||
|
packet.ip.saddr = saddr;
|
||||||
|
packet.ip.daddr = daddr;
|
||||||
|
packet.ip.protocol = IPPROTO_UDP;
|
||||||
|
packet.ip.tot_len = packet.udp.len;
|
||||||
|
temp = packet.udp.check;
|
||||||
|
packet.udp.check = 0;
|
||||||
|
sum = finish_sum(checksum(&packet, nread, 0));
|
||||||
|
packet.udp.check = temp;
|
||||||
|
if (!sum)
|
||||||
|
sum = finish_sum(sum);
|
||||||
|
if (temp != sum) {
|
||||||
|
printf("UDP header checksum failure (0x%x should be 0x%x)\n", sum, temp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(msg, &packet.dhcp, dhcp_size);
|
||||||
|
return dhcp_size;
|
||||||
|
}
|
25
package/wwan/app/fibocom-dial/src/libmnl/dhcp/packet.h
Executable file
25
package/wwan/app/fibocom-dial/src/libmnl/dhcp/packet.h
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WIFI_PACKET_H_
|
||||||
|
#define _WIFI_PACKET_H_
|
||||||
|
|
||||||
|
int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
|
||||||
|
int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
|
||||||
|
uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
|
||||||
|
int receive_packet(int s, struct dhcp_msg *msg);
|
||||||
|
|
||||||
|
#endif
|
748
package/wwan/app/fibocom-dial/src/libmnl/ifutils.c
Executable file
748
package/wwan/app/fibocom-dial/src/libmnl/ifutils.c
Executable file
@ -0,0 +1,748 @@
|
|||||||
|
/* This example is placed in the public domain. */
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include <linux/if_link.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
//#include <linux/if.h>
|
||||||
|
|
||||||
|
#include "libmnl.h"
|
||||||
|
#include "ifutils.h"
|
||||||
|
|
||||||
|
#define ERRMSG(v...) printf("%s-%d: error=%s %s\n", __func__, __LINE__, strerror(errno), ##v)
|
||||||
|
extern void dbg_time(const char *fmt, ...);
|
||||||
|
int mask_to_prefix_v4(uint32_t mask)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
while (mask)
|
||||||
|
{
|
||||||
|
mask = mask & (mask - 1);
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ipaddr_to_string_v4(in_addr_t ipaddr)
|
||||||
|
{
|
||||||
|
static char buf[INET6_ADDRSTRLEN] = {'\0'};
|
||||||
|
buf[0] = '\0';
|
||||||
|
uint32_t addr = ipaddr;
|
||||||
|
return inet_ntop(AF_INET, &addr, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *ipaddr_to_string_v6(uint8_t *ipaddr)
|
||||||
|
{
|
||||||
|
static char buf[INET6_ADDRSTRLEN] = {'\0'};
|
||||||
|
buf[0] = '\0';
|
||||||
|
return inet_ntop(AF_INET6, ipaddr, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ifc_init_ifr(const char *name, struct ifreq *ifr)
|
||||||
|
{
|
||||||
|
memset(ifr, 0, sizeof(struct ifreq));
|
||||||
|
strncpy(ifr->ifr_name, name, IFNAMSIZ);
|
||||||
|
ifr->ifr_name[IFNAMSIZ - 1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_get_hwaddr(const char *name, void *ptr)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct ifreq ifr;
|
||||||
|
ifc_init_ifr(name, &ifr);
|
||||||
|
|
||||||
|
int ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||||
|
if (ifc_ctl_sock < 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
|
||||||
|
if (r < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int if_act_on_link(const char *ifname, int state)
|
||||||
|
{
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct ifinfomsg *ifm;
|
||||||
|
int ret;
|
||||||
|
unsigned int seq, portid, change = 0, flags = 0;
|
||||||
|
static int oldstate = -1;
|
||||||
|
|
||||||
|
if (state == oldstate)
|
||||||
|
return 0;
|
||||||
|
oldstate = state;
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
change |= IFF_UP;
|
||||||
|
flags |= IFF_UP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
change |= IFF_UP;
|
||||||
|
flags &= ~IFF_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
nlh->nlmsg_type = RTM_NEWLINK;
|
||||||
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||||
|
nlh->nlmsg_seq = seq = time(NULL);
|
||||||
|
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
|
||||||
|
ifm->ifi_family = AF_UNSPEC;
|
||||||
|
ifm->ifi_change = change;
|
||||||
|
ifm->ifi_flags = flags;
|
||||||
|
|
||||||
|
mnl_attr_put_str(nlh, IFLA_IFNAME, ifname);
|
||||||
|
|
||||||
|
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||||
|
if (nl == NULL)
|
||||||
|
{
|
||||||
|
ERRMSG("mnl_socket_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
portid = mnl_socket_get_portid(nl);
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_recvfrom");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_cb_run");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_link_up(const char *ifname)
|
||||||
|
{
|
||||||
|
dbg_time("if_link_up %s",ifname);
|
||||||
|
return if_act_on_link(ifname, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_link_down(const char *ifname)
|
||||||
|
{
|
||||||
|
|
||||||
|
dbg_time("if_link_down %s",ifname);
|
||||||
|
return if_act_on_link(ifname, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_mtu(const char *ifname, uint32_t mtu)
|
||||||
|
{
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
unsigned int seq, portid;
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct ifinfomsg *ifm;
|
||||||
|
int ret;
|
||||||
|
int iface;
|
||||||
|
static uint32_t oldmtu = 1500;
|
||||||
|
|
||||||
|
if (mtu == oldmtu)
|
||||||
|
return 0;
|
||||||
|
oldmtu = mtu;
|
||||||
|
|
||||||
|
iface = if_nametoindex(ifname);
|
||||||
|
if (iface == 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" if_nametoindex");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
nlh->nlmsg_type = RTM_NEWLINK;
|
||||||
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||||
|
nlh->nlmsg_seq = seq = time(NULL);
|
||||||
|
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg));
|
||||||
|
ifm->ifi_family = AF_UNSPEC;
|
||||||
|
ifm->ifi_index = iface;
|
||||||
|
ifm->ifi_change = 0xFFFFFFFF;
|
||||||
|
ifm->ifi_type = 0;
|
||||||
|
ifm->ifi_flags = IFF_NOARP | IFF_MULTICAST;
|
||||||
|
|
||||||
|
mnl_attr_put_u32(nlh, IFLA_MTU, mtu);
|
||||||
|
|
||||||
|
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||||
|
if (nl == NULL)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
portid = mnl_socket_get_portid(nl);
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_recvfrom");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_cb_run");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the ip addr object
|
||||||
|
*
|
||||||
|
* @param operate
|
||||||
|
* 0 -> add address on interface
|
||||||
|
* 1 -> delete address on interface
|
||||||
|
* @param ifname
|
||||||
|
* @param ipaddr
|
||||||
|
* @param prefix
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
static int if_act_on_addr(bool operate, int proto, const char *ifname, addr_t *ipaddr, uint32_t prefix)
|
||||||
|
{
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct ifaddrmsg *ifm;
|
||||||
|
uint32_t seq, portid;
|
||||||
|
int ret, family = proto;
|
||||||
|
|
||||||
|
int iface;
|
||||||
|
|
||||||
|
iface = if_nametoindex(ifname);
|
||||||
|
if (iface == 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" if_nametoindex");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
if (operate)
|
||||||
|
nlh->nlmsg_type = RTM_NEWADDR;
|
||||||
|
else
|
||||||
|
nlh->nlmsg_type = RTM_DELADDR;
|
||||||
|
|
||||||
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK;
|
||||||
|
nlh->nlmsg_seq = seq = time(NULL);
|
||||||
|
|
||||||
|
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifaddrmsg));
|
||||||
|
|
||||||
|
ifm->ifa_family = family;
|
||||||
|
ifm->ifa_prefixlen = prefix;
|
||||||
|
ifm->ifa_flags = IFA_F_PERMANENT;
|
||||||
|
|
||||||
|
ifm->ifa_scope = RT_SCOPE_UNIVERSE;
|
||||||
|
ifm->ifa_index = iface;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The exact meaning of IFA_LOCAL and IFA_ADDRESS depend
|
||||||
|
* on the address family being used and the device type.
|
||||||
|
* For broadcast devices (like the interfaces we use),
|
||||||
|
* for IPv4 we specify both and they are used interchangeably.
|
||||||
|
* For IPv6, only IFA_ADDRESS needs to be set.
|
||||||
|
*/
|
||||||
|
if (family == AF_INET)
|
||||||
|
{
|
||||||
|
mnl_attr_put_u32(nlh, IFA_LOCAL, ipaddr->ip);
|
||||||
|
mnl_attr_put_u32(nlh, IFA_ADDRESS, ipaddr->ip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, IFA_ADDRESS, sizeof(struct in6_addr), ipaddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||||
|
if (nl == NULL)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
portid = mnl_socket_get_portid(nl);
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_recvfrom");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_cb_run");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
addr.ip = ipaddr;
|
||||||
|
return if_act_on_addr(1, AF_INET, ifname, &addr, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_addr_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
addr.ip = ipaddr;
|
||||||
|
return if_act_on_addr(0, AF_INET, ifname, &addr, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memcpy(&addr.ip6, ipaddr, 16);
|
||||||
|
return if_act_on_addr(1, AF_INET6, ifname, &addr, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_addr_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memcpy(&addr.ip6, ipaddr, 16);
|
||||||
|
return if_act_on_addr(0, AF_INET6, ifname, &addr, prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int data_attr_cb(const struct nlattr *attr, void *data)
|
||||||
|
{
|
||||||
|
const struct nlattr **tb = data;
|
||||||
|
int type = mnl_attr_get_type(attr);
|
||||||
|
|
||||||
|
/* skip unsupported attribute in user-space */
|
||||||
|
if (mnl_attr_type_valid(attr, IFA_MAX) < 0)
|
||||||
|
return MNL_CB_OK;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IFA_ADDRESS:
|
||||||
|
if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_attr_validate");
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tb[type] = attr;
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int data_cb(const struct nlmsghdr *nlh, void *data)
|
||||||
|
{
|
||||||
|
struct nlattr *tb[IFA_MAX + 1] = {};
|
||||||
|
struct ifaddrmsg *ifa = mnl_nlmsg_get_payload(nlh);
|
||||||
|
struct addrinfo_t *addrinfo = (struct addrinfo_t *)data;
|
||||||
|
void *addr = NULL;
|
||||||
|
|
||||||
|
mnl_attr_parse(nlh, sizeof(*ifa), data_attr_cb, tb);
|
||||||
|
if (tb[IFA_ADDRESS])
|
||||||
|
{
|
||||||
|
char out[INET6_ADDRSTRLEN];
|
||||||
|
|
||||||
|
addr = mnl_attr_get_payload(tb[IFLA_ADDRESS]);
|
||||||
|
addr = mnl_attr_get_payload(tb[IFA_ADDRESS]);
|
||||||
|
if (!inet_ntop(ifa->ifa_family, addr, out, sizeof(out)))
|
||||||
|
ERRMSG("inet_ntop");
|
||||||
|
// printf("%d %d-> %d %s\n", addrinfo->iface, ifa->ifa_index, ifa->ifa_scope, out);
|
||||||
|
|
||||||
|
addrinfo->addrs[addrinfo->num].prefix = ifa->ifa_prefixlen;
|
||||||
|
if (ifa->ifa_index == addrinfo->iface)
|
||||||
|
{
|
||||||
|
if (ifa->ifa_family == AF_INET6)
|
||||||
|
memcpy(addrinfo->addrs[addrinfo->num].address.ip6.s6_addr, addr, 16);
|
||||||
|
if (ifa->ifa_family == AF_INET)
|
||||||
|
memcpy(&(addrinfo->addrs[addrinfo->num].address.ip), addr, 4);
|
||||||
|
addrinfo->num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ifa->ifa_scope
|
||||||
|
// 0: global
|
||||||
|
// 200: site
|
||||||
|
// 253: link
|
||||||
|
// 254: host
|
||||||
|
// 255: nowhere
|
||||||
|
|
||||||
|
return MNL_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param ifname
|
||||||
|
* @param proto
|
||||||
|
* AF_INET -> for IPv4
|
||||||
|
* AF_INET6 -> for IPv6
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
static int if_get_addr(const char *ifname, int proto, struct addrinfo_t *addrinfo)
|
||||||
|
{
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
unsigned int seq, portid;
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct rtgenmsg *rt;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
addrinfo->iface = if_nametoindex(ifname);
|
||||||
|
if (addrinfo->iface == 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" if_nametoindex");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
nlh->nlmsg_type = RTM_GETADDR;
|
||||||
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||||
|
nlh->nlmsg_seq = seq = time(NULL);
|
||||||
|
rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg));
|
||||||
|
if (proto == AF_INET)
|
||||||
|
rt->rtgen_family = AF_INET;
|
||||||
|
else if (proto == AF_INET6)
|
||||||
|
rt->rtgen_family = AF_INET6;
|
||||||
|
|
||||||
|
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||||
|
if (nl == NULL)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
portid = mnl_socket_get_portid(nl);
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
while (ret > 0)
|
||||||
|
{
|
||||||
|
ret = mnl_cb_run(buf, ret, seq, portid, data_cb, addrinfo);
|
||||||
|
if (ret <= MNL_CB_STOP)
|
||||||
|
break;
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
ERRMSG(" error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_flush_v4_addr(const char *ifname)
|
||||||
|
{
|
||||||
|
struct addrinfo_t addrinfo;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
memset(&addrinfo, 0, sizeof(struct addrinfo_t));
|
||||||
|
if_get_addr(ifname, AF_INET, &addrinfo);
|
||||||
|
for (; i < addrinfo.num; i++)
|
||||||
|
{
|
||||||
|
// printf("remove address: %s\n", ipaddr_to_string_v4(addrinfo.addrs[i].address.ip));
|
||||||
|
if_del_addr_v4(ifname, addrinfo.addrs[i].address.ip, addrinfo.addrs[i].prefix);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_flush_v6_addr(const char *ifname)
|
||||||
|
{
|
||||||
|
struct addrinfo_t addrinfo;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
memset(&addrinfo, 0, sizeof(struct addrinfo_t));
|
||||||
|
if_get_addr(ifname, AF_INET6, &addrinfo);
|
||||||
|
for (; i < addrinfo.num; i++)
|
||||||
|
{
|
||||||
|
// printf("remove address: %s\n", ipaddr_to_string_v6(addrinfo.addrs[i].address.ip6.s6_addr));
|
||||||
|
if_del_addr_v6(ifname, addrinfo.addrs[i].address.ip6.s6_addr, addrinfo.addrs[i].prefix);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the route addr object
|
||||||
|
* Usage:
|
||||||
|
* iface destination cidr [gateway]
|
||||||
|
* Example:
|
||||||
|
* eth0 10.0.1.12 32 10.0.1.11
|
||||||
|
* eth0 ffff::10.0.1.12 128 fdff::1
|
||||||
|
* @param operate
|
||||||
|
* add or del
|
||||||
|
* @param ifname
|
||||||
|
* @param dstaddr
|
||||||
|
* @param prefix
|
||||||
|
* @param gwaddr
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int if_act_on_route(bool operate, int proto, const char *ifname, addr_t *dstaddr, uint32_t prefix, addr_t *gwaddr)
|
||||||
|
{
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct rtmsg *rtm;
|
||||||
|
uint32_t seq, portid;
|
||||||
|
int iface, ret, family = proto;
|
||||||
|
|
||||||
|
iface = if_nametoindex(ifname);
|
||||||
|
if (iface == 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" if_nametoindex");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = mnl_nlmsg_put_header(buf);
|
||||||
|
if (operate)
|
||||||
|
nlh->nlmsg_type = RTM_NEWROUTE;
|
||||||
|
else
|
||||||
|
nlh->nlmsg_type = RTM_DELROUTE;
|
||||||
|
|
||||||
|
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
|
||||||
|
nlh->nlmsg_seq = seq = time(NULL);
|
||||||
|
|
||||||
|
rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
|
||||||
|
rtm->rtm_family = family;
|
||||||
|
rtm->rtm_dst_len = prefix;
|
||||||
|
rtm->rtm_src_len = 0;
|
||||||
|
rtm->rtm_tos = 0;
|
||||||
|
rtm->rtm_protocol = RTPROT_STATIC;
|
||||||
|
rtm->rtm_table = RT_TABLE_MAIN;
|
||||||
|
rtm->rtm_type = RTN_UNICAST;
|
||||||
|
/* is there any gateway? */
|
||||||
|
rtm->rtm_scope = gwaddr ? RT_SCOPE_UNIVERSE : RT_SCOPE_LINK;
|
||||||
|
rtm->rtm_flags = 0;
|
||||||
|
|
||||||
|
if (family == AF_INET)
|
||||||
|
mnl_attr_put_u32(nlh, RTA_DST, dstaddr->ip);
|
||||||
|
else
|
||||||
|
mnl_attr_put(nlh, RTA_DST, sizeof(struct in6_addr), dstaddr);
|
||||||
|
|
||||||
|
mnl_attr_put_u32(nlh, RTA_OIF, iface);
|
||||||
|
if (gwaddr)
|
||||||
|
{
|
||||||
|
if (family == AF_INET)
|
||||||
|
mnl_attr_put_u32(nlh, RTA_GATEWAY, gwaddr->ip);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mnl_attr_put(nlh, RTA_GATEWAY, sizeof(struct in6_addr), gwaddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nl = mnl_socket_open(NETLINK_ROUTE);
|
||||||
|
if (nl == NULL)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_open");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_bind");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
portid = mnl_socket_get_portid(nl);
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_sendto");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_socket_recvfrom");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" mnl_cb_run");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mnl_socket_close(nl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_default_route_v4(const char *ifname)
|
||||||
|
{
|
||||||
|
return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_default_route_v4(const char *ifname)
|
||||||
|
{
|
||||||
|
return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_default_route_v6(const char *ifname)
|
||||||
|
{
|
||||||
|
return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_default_route_v6(const char *ifname)
|
||||||
|
{
|
||||||
|
return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the default gwaddr object
|
||||||
|
* set default gw
|
||||||
|
* @param operate
|
||||||
|
* @param ifname
|
||||||
|
* @param gwaddr
|
||||||
|
* gateway ip
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memset(&addr, 0, sizeof(addr_t));
|
||||||
|
addr.ip = gwaddr;
|
||||||
|
return if_act_on_route(1, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memset(&addr, 0, sizeof(addr_t));
|
||||||
|
addr.ip = gwaddr;
|
||||||
|
return if_act_on_route(0, AF_INET, ifname, (addr_t *)&in6addr_any, 0, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memset(&addr, 0, sizeof(addr_t));
|
||||||
|
memcpy(&addr.ip6, gwaddr, 16);
|
||||||
|
return if_act_on_route(1, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr)
|
||||||
|
{
|
||||||
|
addr_t addr;
|
||||||
|
memset(&addr, 0, sizeof(addr_t));
|
||||||
|
memcpy(&addr.ip6, gwaddr, 16);
|
||||||
|
return if_act_on_route(0, AF_INET6, ifname, (addr_t *)&in6addr_any, 0, &addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_dns(const char *dns1, const char *dns2)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
char buf[128] = {'\0'};
|
||||||
|
int fd = open("/etc/resolv.conf", O_CREAT | O_WRONLY | O_TRUNC);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" fail to open /etc/resolv.conf");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dns1)
|
||||||
|
snprintf(buf, sizeof(buf), "nameserver %s\n", dns1);
|
||||||
|
if (dns2)
|
||||||
|
snprintf(buf, sizeof(buf), "nameserver %s\n", dns2);
|
||||||
|
ret = write(fd, buf, strlen(buf));
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ERRMSG(" write dns");
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return ret > 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix,
|
||||||
|
in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2)
|
||||||
|
{
|
||||||
|
if_link_up(ifname);
|
||||||
|
sleep(2);
|
||||||
|
if_set_addr_v4(ifname, ipaddr, prefix);
|
||||||
|
if_set_default_route_v4(ifname);
|
||||||
|
if_set_dns(ipaddr_to_string_v4(dns1), ipaddr_to_string_v4(dns2));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix,
|
||||||
|
uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2)
|
||||||
|
{
|
||||||
|
if_link_up(ifname);
|
||||||
|
sleep(2);
|
||||||
|
if_set_addr_v6(ifname, ipaddr, prefix);
|
||||||
|
if_set_default_route_v6(ifname);
|
||||||
|
if_set_dns(ipaddr_to_string_v6(dns1), ipaddr_to_string_v6(dns2));
|
||||||
|
return 0;
|
||||||
|
}
|
53
package/wwan/app/fibocom-dial/src/libmnl/ifutils.h
Executable file
53
package/wwan/app/fibocom-dial/src/libmnl/ifutils.h
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef __IFUTILS_H__
|
||||||
|
#define __IFUTILS_H__
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
in_addr_t ip;
|
||||||
|
struct in6_addr ip6;
|
||||||
|
} addr_t;
|
||||||
|
|
||||||
|
#define MAX_IP_NUM 32
|
||||||
|
struct addrinfo_t
|
||||||
|
{
|
||||||
|
int iface;
|
||||||
|
int num;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int prefix;
|
||||||
|
addr_t address;
|
||||||
|
} addrs[MAX_IP_NUM];
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *ipaddr_to_string_v4(in_addr_t ipaddr);
|
||||||
|
const char *ipaddr_to_string_v6(uint8_t *ipaddr);
|
||||||
|
int mask_to_prefix_v4(in_addr_t mask);
|
||||||
|
|
||||||
|
int if_get_hwaddr(const char *name, void *ptr);
|
||||||
|
|
||||||
|
int if_link_down(const char *ifname);
|
||||||
|
int if_link_up(const char *ifname);
|
||||||
|
int if_set_mtu(const char *ifname, uint32_t mtu);
|
||||||
|
|
||||||
|
int if_set_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen);
|
||||||
|
int if_del_addr_v4(const char *name, in_addr_t address, uint32_t prefixlen);
|
||||||
|
int if_set_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen);
|
||||||
|
int if_del_addr_v6(const char *name, uint8_t *address, uint32_t prefixlen);
|
||||||
|
int if_flush_v4_addr(const char *ifname);
|
||||||
|
int if_flush_v6_addr(const char *ifname);
|
||||||
|
|
||||||
|
int if_set_route_gw_v4(const char *ifname, in_addr_t gwaddr);
|
||||||
|
int if_del_route_gw_v4(const char *ifname, in_addr_t gwaddr);
|
||||||
|
int if_set_default_route_v4(const char *ifname);
|
||||||
|
int if_del_default_route_v4(const char *ifname);
|
||||||
|
|
||||||
|
int if_set_route_gw_v6(const char *ifname, uint8_t *gwaddr);
|
||||||
|
int if_del_route_gw_v6(const char *ifname, uint8_t *gwaddr);
|
||||||
|
int if_set_default_route_v6(const char *ifname);
|
||||||
|
int if_del_default_route_v6(const char *ifname);
|
||||||
|
|
||||||
|
int if_set_network_v4(const char *ifname, in_addr_t ipaddr, uint32_t prefix,
|
||||||
|
in_addr_t gwaddr, in_addr_t dns1, in_addr_t dns2);
|
||||||
|
int if_set_network_v6(const char *ifname, uint8_t *ipaddr, uint32_t prefix,
|
||||||
|
uint8_t *gwaddr, uint8_t *dns1, uint8_t *dns2);
|
||||||
|
|
||||||
|
#endif //__IFUTILS_H__
|
202
package/wwan/app/fibocom-dial/src/libmnl/libmnl.h
Executable file
202
package/wwan/app/fibocom-dial/src/libmnl/libmnl.h
Executable file
@ -0,0 +1,202 @@
|
|||||||
|
#ifndef _LIBMNL_H_
|
||||||
|
#define _LIBMNL_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h> /* for sa_family_t */
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Netlink socket API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MNL_SOCKET_AUTOPID 0
|
||||||
|
#define MNL_SOCKET_BUFFER_SIZE (sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L)
|
||||||
|
#define MNL_SOCKET_DUMP_SIZE 32768
|
||||||
|
|
||||||
|
struct mnl_socket;
|
||||||
|
|
||||||
|
extern struct mnl_socket *mnl_socket_open(int bus);
|
||||||
|
extern struct mnl_socket *mnl_socket_open2(int bus, int flags);
|
||||||
|
extern struct mnl_socket *mnl_socket_fdopen(int fd);
|
||||||
|
extern int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid);
|
||||||
|
extern int mnl_socket_close(struct mnl_socket *nl);
|
||||||
|
extern int mnl_socket_get_fd(const struct mnl_socket *nl);
|
||||||
|
extern unsigned int mnl_socket_get_portid(const struct mnl_socket *nl);
|
||||||
|
extern ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *req, size_t siz);
|
||||||
|
extern ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf, size_t siz);
|
||||||
|
extern int mnl_socket_setsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t len);
|
||||||
|
extern int mnl_socket_getsockopt(const struct mnl_socket *nl, int type, void *buf, socklen_t *len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Netlink message API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MNL_ALIGNTO 4
|
||||||
|
#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))
|
||||||
|
#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))
|
||||||
|
|
||||||
|
extern size_t mnl_nlmsg_size(size_t len);
|
||||||
|
extern size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh);
|
||||||
|
|
||||||
|
/* Netlink message header builder */
|
||||||
|
extern struct nlmsghdr *mnl_nlmsg_put_header(void *buf);
|
||||||
|
extern void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size);
|
||||||
|
|
||||||
|
/* Netlink message iterators */
|
||||||
|
extern bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len);
|
||||||
|
extern struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len);
|
||||||
|
|
||||||
|
/* Netlink sequence tracking */
|
||||||
|
extern bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq);
|
||||||
|
|
||||||
|
/* Netlink portID checking */
|
||||||
|
extern bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid);
|
||||||
|
|
||||||
|
/* Netlink message getters */
|
||||||
|
extern void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh);
|
||||||
|
extern void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset);
|
||||||
|
extern void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh);
|
||||||
|
|
||||||
|
/* Netlink message printer */
|
||||||
|
extern void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen, size_t extra_header_size);
|
||||||
|
|
||||||
|
/* Message batch helpers */
|
||||||
|
struct mnl_nlmsg_batch;
|
||||||
|
extern struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf, size_t bufsiz);
|
||||||
|
extern bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b);
|
||||||
|
extern void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b);
|
||||||
|
extern size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b);
|
||||||
|
extern void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b);
|
||||||
|
extern void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b);
|
||||||
|
extern void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b);
|
||||||
|
extern bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Netlink attributes API
|
||||||
|
*/
|
||||||
|
#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))
|
||||||
|
|
||||||
|
/* TLV attribute getters */
|
||||||
|
extern uint16_t mnl_attr_get_type(const struct nlattr *attr);
|
||||||
|
extern uint16_t mnl_attr_get_len(const struct nlattr *attr);
|
||||||
|
extern uint16_t mnl_attr_get_payload_len(const struct nlattr *attr);
|
||||||
|
extern void *mnl_attr_get_payload(const struct nlattr *attr);
|
||||||
|
extern uint8_t mnl_attr_get_u8(const struct nlattr *attr);
|
||||||
|
extern uint16_t mnl_attr_get_u16(const struct nlattr *attr);
|
||||||
|
extern uint32_t mnl_attr_get_u32(const struct nlattr *attr);
|
||||||
|
extern uint64_t mnl_attr_get_u64(const struct nlattr *attr);
|
||||||
|
extern const char *mnl_attr_get_str(const struct nlattr *attr);
|
||||||
|
|
||||||
|
/* TLV attribute putters */
|
||||||
|
extern void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len, const void *data);
|
||||||
|
extern void mnl_attr_put_u8(struct nlmsghdr *nlh, uint16_t type, uint8_t data);
|
||||||
|
extern void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data);
|
||||||
|
extern void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data);
|
||||||
|
extern void mnl_attr_put_u64(struct nlmsghdr *nlh, uint16_t type, uint64_t data);
|
||||||
|
extern void mnl_attr_put_str(struct nlmsghdr *nlh, uint16_t type, const char *data);
|
||||||
|
extern void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data);
|
||||||
|
|
||||||
|
/* TLV attribute putters with buffer boundary checkings */
|
||||||
|
extern bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, size_t len, const void *data);
|
||||||
|
extern bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint8_t data);
|
||||||
|
extern bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint16_t data);
|
||||||
|
extern bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint32_t data);
|
||||||
|
extern bool mnl_attr_put_u64_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, uint64_t data);
|
||||||
|
extern bool mnl_attr_put_str_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
|
||||||
|
extern bool mnl_attr_put_strz_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type, const char *data);
|
||||||
|
|
||||||
|
/* TLV attribute nesting */
|
||||||
|
extern struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type);
|
||||||
|
extern struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen, uint16_t type);
|
||||||
|
extern void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start);
|
||||||
|
extern void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start);
|
||||||
|
|
||||||
|
/* TLV validation */
|
||||||
|
extern int mnl_attr_type_valid(const struct nlattr *attr, uint16_t maxtype);
|
||||||
|
|
||||||
|
enum mnl_attr_data_type {
|
||||||
|
MNL_TYPE_UNSPEC,
|
||||||
|
MNL_TYPE_U8,
|
||||||
|
MNL_TYPE_U16,
|
||||||
|
MNL_TYPE_U32,
|
||||||
|
MNL_TYPE_U64,
|
||||||
|
MNL_TYPE_STRING,
|
||||||
|
MNL_TYPE_FLAG,
|
||||||
|
MNL_TYPE_MSECS,
|
||||||
|
MNL_TYPE_NESTED,
|
||||||
|
MNL_TYPE_NESTED_COMPAT,
|
||||||
|
MNL_TYPE_NUL_STRING,
|
||||||
|
MNL_TYPE_BINARY,
|
||||||
|
MNL_TYPE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type);
|
||||||
|
extern int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type, size_t len);
|
||||||
|
|
||||||
|
/* TLV iterators */
|
||||||
|
extern bool mnl_attr_ok(const struct nlattr *attr, int len);
|
||||||
|
extern struct nlattr *mnl_attr_next(const struct nlattr *attr);
|
||||||
|
|
||||||
|
#define mnl_attr_for_each(attr, nlh, offset) \
|
||||||
|
for ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \
|
||||||
|
mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \
|
||||||
|
(attr) = mnl_attr_next(attr))
|
||||||
|
|
||||||
|
#define mnl_attr_for_each_nested(attr, nest) \
|
||||||
|
for ((attr) = mnl_attr_get_payload(nest); \
|
||||||
|
mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \
|
||||||
|
(attr) = mnl_attr_next(attr))
|
||||||
|
|
||||||
|
#define mnl_attr_for_each_payload(payload, payload_size) \
|
||||||
|
for ((attr) = (payload); \
|
||||||
|
mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \
|
||||||
|
(attr) = mnl_attr_next(attr))
|
||||||
|
|
||||||
|
/* TLV callback-based attribute parsers */
|
||||||
|
typedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);
|
||||||
|
|
||||||
|
extern int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset, mnl_attr_cb_t cb, void *data);
|
||||||
|
extern int mnl_attr_parse_nested(const struct nlattr *attr, mnl_attr_cb_t cb, void *data);
|
||||||
|
extern int mnl_attr_parse_payload(const void *payload, size_t payload_len, mnl_attr_cb_t cb, void *data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* callback API
|
||||||
|
*/
|
||||||
|
#define MNL_CB_ERROR -1
|
||||||
|
#define MNL_CB_STOP 0
|
||||||
|
#define MNL_CB_OK 1
|
||||||
|
|
||||||
|
typedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);
|
||||||
|
|
||||||
|
extern int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,
|
||||||
|
unsigned int portid, mnl_cb_t cb_data, void *data);
|
||||||
|
|
||||||
|
extern int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,
|
||||||
|
unsigned int portid, mnl_cb_t cb_data, void *data,
|
||||||
|
const mnl_cb_t *cb_ctl_array,
|
||||||
|
unsigned int cb_ctl_array_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* other declarations
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SOL_NETLINK
|
||||||
|
#define SOL_NETLINK 270
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MNL_ARRAY_SIZE
|
||||||
|
#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
556
package/wwan/app/fibocom-dial/src/libmnl/nlmsg.c
Executable file
556
package/wwan/app/fibocom-dial/src/libmnl/nlmsg.c
Executable file
@ -0,0 +1,556 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "libmnl.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup nlmsg Netlink message helpers
|
||||||
|
*
|
||||||
|
* Netlink message:
|
||||||
|
* \verbatim
|
||||||
|
|<----------------- 4 bytes ------------------->|
|
||||||
|
|<----- 2 bytes ------>|<------- 2 bytes ------>|
|
||||||
|
|-----------------------------------------------|
|
||||||
|
| Message length (including header) |
|
||||||
|
|-----------------------------------------------|
|
||||||
|
| Message type | Message flags |
|
||||||
|
|-----------------------------------------------|
|
||||||
|
| Message sequence number |
|
||||||
|
|-----------------------------------------------|
|
||||||
|
| Netlink PortID |
|
||||||
|
|-----------------------------------------------|
|
||||||
|
| |
|
||||||
|
. Payload .
|
||||||
|
|_______________________________________________|
|
||||||
|
\endverbatim
|
||||||
|
*
|
||||||
|
* There is usually an extra header after the the Netlink header (at the
|
||||||
|
* beginning of the payload). This extra header is specific of the Netlink
|
||||||
|
* subsystem. After this extra header, it comes the sequence of attributes
|
||||||
|
* that are expressed in Type-Length-Value (TLV) format.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_size - calculate the size of Netlink message (without alignment)
|
||||||
|
* \param len length of the Netlink payload
|
||||||
|
*
|
||||||
|
* This function returns the size of a netlink message (header plus payload)
|
||||||
|
* without alignment.
|
||||||
|
*/
|
||||||
|
size_t mnl_nlmsg_size(size_t len)
|
||||||
|
{
|
||||||
|
return len + MNL_NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_get_payload_len - get the length of the Netlink payload
|
||||||
|
* \param nlh pointer to the header of the Netlink message
|
||||||
|
*
|
||||||
|
* This function returns the Length of the netlink payload, ie. the length
|
||||||
|
* of the full message minus the size of the Netlink header.
|
||||||
|
*/
|
||||||
|
size_t mnl_nlmsg_get_payload_len(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return nlh->nlmsg_len - MNL_NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_put_header - reserve and prepare room for Netlink header
|
||||||
|
* \param buf memory already allocated to store the Netlink header
|
||||||
|
*
|
||||||
|
* This function sets to zero the room that is required to put the Netlink
|
||||||
|
* header in the memory buffer passed as parameter. This function also
|
||||||
|
* initializes the nlmsg_len field to the size of the Netlink header. This
|
||||||
|
* function returns a pointer to the Netlink header structure.
|
||||||
|
*/
|
||||||
|
struct nlmsghdr *mnl_nlmsg_put_header(void *buf)
|
||||||
|
{
|
||||||
|
int len = MNL_ALIGN(sizeof(struct nlmsghdr));
|
||||||
|
struct nlmsghdr *nlh = buf;
|
||||||
|
|
||||||
|
memset(buf, 0, len);
|
||||||
|
nlh->nlmsg_len = len;
|
||||||
|
return nlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_put_extra_header - reserve and prepare room for an extra header
|
||||||
|
* \param nlh pointer to Netlink header
|
||||||
|
* \param size size of the extra header that we want to put
|
||||||
|
*
|
||||||
|
* This function sets to zero the room that is required to put the extra
|
||||||
|
* header after the initial Netlink header. This function also increases
|
||||||
|
* the nlmsg_len field. You have to invoke mnl_nlmsg_put_header() before
|
||||||
|
* you call this function. This function returns a pointer to the extra
|
||||||
|
* header.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
char *ptr = (char *)nlh + nlh->nlmsg_len;
|
||||||
|
size_t len = MNL_ALIGN(size);
|
||||||
|
nlh->nlmsg_len += len;
|
||||||
|
memset(ptr, 0, len);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_get_payload - get a pointer to the payload of the netlink message
|
||||||
|
* \param nlh pointer to a netlink header
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the payload of the netlink message.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return (void *)nlh + MNL_NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_get_payload_offset - get a pointer to the payload of the message
|
||||||
|
* \param nlh pointer to a netlink header
|
||||||
|
* \param offset offset to the payload of the attributes TLV set
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the payload of the netlink message plus
|
||||||
|
* a given offset.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh,
|
||||||
|
size_t offset)
|
||||||
|
{
|
||||||
|
return (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_ok - check a there is room for netlink message
|
||||||
|
* \param nlh netlink message that we want to check
|
||||||
|
* \param len remaining bytes in a buffer that contains the netlink message
|
||||||
|
*
|
||||||
|
* This function is used to check that a buffer that contains a netlink
|
||||||
|
* message has enough room for the netlink message that it stores, ie. this
|
||||||
|
* function can be used to verify that a netlink message is not malformed nor
|
||||||
|
* truncated.
|
||||||
|
*
|
||||||
|
* This function does not set errno in case of error since it is intended
|
||||||
|
* for iterations. Thus, it returns true on success and false on error.
|
||||||
|
*
|
||||||
|
* The len parameter may become negative in malformed messages during message
|
||||||
|
* iteration, that is why we use a signed integer.
|
||||||
|
*/
|
||||||
|
bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)
|
||||||
|
{
|
||||||
|
return len >= (int)sizeof(struct nlmsghdr) &&
|
||||||
|
nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
|
||||||
|
(int)nlh->nlmsg_len <= len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_next - get the next netlink message in a multipart message
|
||||||
|
* \param nlh current netlink message that we are handling
|
||||||
|
* \param len length of the remaining bytes in the buffer (passed by reference).
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the next netlink message that is part
|
||||||
|
* of a multi-part netlink message. Netlink can batch several messages into
|
||||||
|
* one buffer so that the receiver has to iterate over the whole set of
|
||||||
|
* Netlink messages.
|
||||||
|
*
|
||||||
|
* You have to use mnl_nlmsg_ok() to check if the next Netlink message is
|
||||||
|
* valid.
|
||||||
|
*/
|
||||||
|
struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh,
|
||||||
|
int *len)
|
||||||
|
{
|
||||||
|
*len -= MNL_ALIGN(nlh->nlmsg_len);
|
||||||
|
return (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_get_payload_tail - get the ending of the netlink message
|
||||||
|
* \param nlh pointer to netlink message
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the netlink message tail. This is useful
|
||||||
|
* to build a message since we continue adding attributes at the end of the
|
||||||
|
* message.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_seq_ok - perform sequence tracking
|
||||||
|
* \param nlh current netlink message that we are handling
|
||||||
|
* \param seq last sequence number used to send a message
|
||||||
|
*
|
||||||
|
* This functions returns true if the sequence tracking is fulfilled, otherwise
|
||||||
|
* false is returned. We skip the tracking for netlink messages whose sequence
|
||||||
|
* number is zero since it is usually reserved for event-based kernel
|
||||||
|
* notifications. On the other hand, if seq is set but the message sequence
|
||||||
|
* number is not set (i.e. this is an event message coming from kernel-space),
|
||||||
|
* then we also skip the tracking. This approach is good if we use the same
|
||||||
|
* socket to send commands to kernel-space (that we want to track) and to
|
||||||
|
* listen to events (that we do not track).
|
||||||
|
*/
|
||||||
|
bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh,
|
||||||
|
unsigned int seq)
|
||||||
|
{
|
||||||
|
return nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_portid_ok - perform portID origin check
|
||||||
|
* \param nlh current netlink message that we are handling
|
||||||
|
* \param portid netlink portid that we want to check
|
||||||
|
*
|
||||||
|
* This functions returns true if the origin is fulfilled, otherwise
|
||||||
|
* false is returned. We skip the tracking for netlink message whose portID
|
||||||
|
* is zero since it is reserved for event-based kernel notifications. On the
|
||||||
|
* other hand, if portid is set but the message PortID is not (i.e. this
|
||||||
|
* is an event message coming from kernel-space), then we also skip the
|
||||||
|
* tracking. This approach is good if we use the same socket to send commands
|
||||||
|
* to kernel-space (that we want to track) and to listen to events (that we
|
||||||
|
* do not track).
|
||||||
|
*/
|
||||||
|
bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh,
|
||||||
|
unsigned int portid)
|
||||||
|
{
|
||||||
|
return nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mnl_nlmsg_fprintf_header(FILE *fd, const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
fprintf(fd, "----------------\t------------------\n");
|
||||||
|
fprintf(fd, "| %.010u |\t| message length |\n", nlh->nlmsg_len);
|
||||||
|
fprintf(fd, "| %.05u | %c%c%c%c |\t| type | flags |\n",
|
||||||
|
nlh->nlmsg_type,
|
||||||
|
nlh->nlmsg_flags & NLM_F_REQUEST ? 'R' : '-',
|
||||||
|
nlh->nlmsg_flags & NLM_F_MULTI ? 'M' : '-',
|
||||||
|
nlh->nlmsg_flags & NLM_F_ACK ? 'A' : '-',
|
||||||
|
nlh->nlmsg_flags & NLM_F_ECHO ? 'E' : '-');
|
||||||
|
fprintf(fd, "| %.010u |\t| sequence number|\n", nlh->nlmsg_seq);
|
||||||
|
fprintf(fd, "| %.010u |\t| port ID |\n", nlh->nlmsg_pid);
|
||||||
|
fprintf(fd, "----------------\t------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mnl_nlmsg_fprintf_payload(FILE *fd, const struct nlmsghdr *nlh,
|
||||||
|
size_t extra_header_size)
|
||||||
|
{
|
||||||
|
int rem = 0;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i=sizeof(struct nlmsghdr); i<nlh->nlmsg_len; i+=4) {
|
||||||
|
char *b = (char *) nlh;
|
||||||
|
struct nlattr *attr = (struct nlattr *) (b+i);
|
||||||
|
|
||||||
|
/* netlink control message. */
|
||||||
|
if (nlh->nlmsg_type < NLMSG_MIN_TYPE) {
|
||||||
|
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
|
||||||
|
0xff & b[i], 0xff & b[i+1],
|
||||||
|
0xff & b[i+2], 0xff & b[i+3]);
|
||||||
|
fprintf(fd, "| |\n");
|
||||||
|
/* special handling for the extra header. */
|
||||||
|
} else if (extra_header_size > 0) {
|
||||||
|
extra_header_size -= 4;
|
||||||
|
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
|
||||||
|
0xff & b[i], 0xff & b[i+1],
|
||||||
|
0xff & b[i+2], 0xff & b[i+3]);
|
||||||
|
fprintf(fd, "| extra header |\n");
|
||||||
|
/* this seems like an attribute header. */
|
||||||
|
} else if (rem == 0 && (attr->nla_type & NLA_TYPE_MASK) != 0) {
|
||||||
|
fprintf(fd, "|%c[%d;%dm"
|
||||||
|
"%.5u"
|
||||||
|
"%c[%dm"
|
||||||
|
"|"
|
||||||
|
"%c[%d;%dm"
|
||||||
|
"%c%c"
|
||||||
|
"%c[%dm"
|
||||||
|
"|"
|
||||||
|
"%c[%d;%dm"
|
||||||
|
"%.5u"
|
||||||
|
"%c[%dm|\t",
|
||||||
|
27, 1, 31,
|
||||||
|
attr->nla_len,
|
||||||
|
27, 0,
|
||||||
|
27, 1, 32,
|
||||||
|
attr->nla_type & NLA_F_NESTED ? 'N' : '-',
|
||||||
|
attr->nla_type &
|
||||||
|
NLA_F_NET_BYTEORDER ? 'B' : '-',
|
||||||
|
27, 0,
|
||||||
|
27, 1, 34,
|
||||||
|
attr->nla_type & NLA_TYPE_MASK,
|
||||||
|
27, 0);
|
||||||
|
fprintf(fd, "|len |flags| type|\n");
|
||||||
|
|
||||||
|
if (!(attr->nla_type & NLA_F_NESTED)) {
|
||||||
|
rem = NLA_ALIGN(attr->nla_len) -
|
||||||
|
sizeof(struct nlattr);
|
||||||
|
}
|
||||||
|
/* this is the attribute payload. */
|
||||||
|
} else if (rem > 0) {
|
||||||
|
rem -= 4;
|
||||||
|
fprintf(fd, "| %.2x %.2x %.2x %.2x |\t",
|
||||||
|
0xff & b[i], 0xff & b[i+1],
|
||||||
|
0xff & b[i+2], 0xff & b[i+3]);
|
||||||
|
fprintf(fd, "| data |");
|
||||||
|
fprintf(fd, "\t %c %c %c %c\n",
|
||||||
|
isprint(b[i]) ? b[i] : ' ',
|
||||||
|
isprint(b[i+1]) ? b[i+1] : ' ',
|
||||||
|
isprint(b[i+2]) ? b[i+2] : ' ',
|
||||||
|
isprint(b[i+3]) ? b[i+3] : ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(fd, "----------------\t------------------\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_fprintf - print netlink message to file
|
||||||
|
* \param fd pointer to file type
|
||||||
|
* \param data pointer to the buffer that contains messages to be printed
|
||||||
|
* \param datalen length of data stored in the buffer
|
||||||
|
* \param extra_header_size size of the extra header (if any)
|
||||||
|
*
|
||||||
|
* This function prints the netlink header to a file handle.
|
||||||
|
* It may be useful for debugging purposes. One example of the output
|
||||||
|
* is the following:
|
||||||
|
*
|
||||||
|
*\verbatim
|
||||||
|
---------------- ------------------
|
||||||
|
| 0000000040 | | message length |
|
||||||
|
| 00016 | R-A- | | type | flags |
|
||||||
|
| 1289148991 | | sequence number|
|
||||||
|
| 0000000000 | | port ID |
|
||||||
|
---------------- ------------------
|
||||||
|
| 00 00 00 00 | | extra header |
|
||||||
|
| 00 00 00 00 | | extra header |
|
||||||
|
| 01 00 00 00 | | extra header |
|
||||||
|
| 01 00 00 00 | | extra header |
|
||||||
|
|00008|--|00003| |len |flags| type|
|
||||||
|
| 65 74 68 30 | | data | e t h 0
|
||||||
|
---------------- ------------------
|
||||||
|
\endverbatim
|
||||||
|
*
|
||||||
|
* This example above shows the netlink message that is send to kernel-space
|
||||||
|
* to set up the link interface eth0. The netlink and attribute header data
|
||||||
|
* are displayed in base 10 whereas the extra header and the attribute payload
|
||||||
|
* are expressed in base 16. The possible flags in the netlink header are:
|
||||||
|
*
|
||||||
|
* - R, that indicates that NLM_F_REQUEST is set.
|
||||||
|
* - M, that indicates that NLM_F_MULTI is set.
|
||||||
|
* - A, that indicates that NLM_F_ACK is set.
|
||||||
|
* - E, that indicates that NLM_F_ECHO is set.
|
||||||
|
*
|
||||||
|
* The lack of one flag is displayed with '-'. On the other hand, the possible
|
||||||
|
* attribute flags available are:
|
||||||
|
*
|
||||||
|
* - N, that indicates that NLA_F_NESTED is set.
|
||||||
|
* - B, that indicates that NLA_F_NET_BYTEORDER is set.
|
||||||
|
*/
|
||||||
|
void mnl_nlmsg_fprintf(FILE *fd, const void *data, size_t datalen,
|
||||||
|
size_t extra_header_size)
|
||||||
|
{
|
||||||
|
const struct nlmsghdr *nlh = data;
|
||||||
|
int len = datalen;
|
||||||
|
|
||||||
|
while (mnl_nlmsg_ok(nlh, len)) {
|
||||||
|
mnl_nlmsg_fprintf_header(fd, nlh);
|
||||||
|
mnl_nlmsg_fprintf_payload(fd, nlh, extra_header_size);
|
||||||
|
nlh = mnl_nlmsg_next(nlh, &len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup batch Netlink message batch helpers
|
||||||
|
*
|
||||||
|
* This library provides helpers to batch several messages into one single
|
||||||
|
* datagram. These helpers do not perform strict memory boundary checkings.
|
||||||
|
*
|
||||||
|
* The following figure represents a Netlink message batch:
|
||||||
|
*
|
||||||
|
* |<-------------- MNL_SOCKET_BUFFER_SIZE ------------->|
|
||||||
|
* |<-------------------- batch ------------------>| |
|
||||||
|
* |-----------|-----------|-----------|-----------|-----------|
|
||||||
|
* |<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|<- nlmsg ->|
|
||||||
|
* |-----------|-----------|-----------|-----------|-----------|
|
||||||
|
* ^ ^
|
||||||
|
* | |
|
||||||
|
* message N message N+1
|
||||||
|
*
|
||||||
|
* To start the batch, you have to call mnl_nlmsg_batch_start() and you can
|
||||||
|
* use mnl_nlmsg_batch_stop() to release it.
|
||||||
|
*
|
||||||
|
* You have to invoke mnl_nlmsg_batch_next() to get room for a new message
|
||||||
|
* in the batch. If this function returns NULL, it means that the last
|
||||||
|
* message that was added (message N+1 in the figure above) does not fit the
|
||||||
|
* batch. Thus, you have to send the batch (which includes until message N)
|
||||||
|
* and, then, you have to call mnl_nlmsg_batch_reset() to re-initialize
|
||||||
|
* the batch (this moves message N+1 to the head of the buffer). For that
|
||||||
|
* reason, the buffer that you have to use to store the batch must be double
|
||||||
|
* of MNL_SOCKET_BUFFER_SIZE to ensure that the last message (message N+1)
|
||||||
|
* that did not fit into the batch is written inside valid memory boundaries.
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct mnl_nlmsg_batch {
|
||||||
|
/* the buffer that is used to store the batch. */
|
||||||
|
void *buf;
|
||||||
|
size_t limit;
|
||||||
|
size_t buflen;
|
||||||
|
/* the current netlink message in the batch. */
|
||||||
|
void *cur;
|
||||||
|
bool overflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_start - initialize a batch
|
||||||
|
* \param buf pointer to the buffer that will store this batch
|
||||||
|
* \param limit maximum size of the batch (should be MNL_SOCKET_BUFFER_SIZE).
|
||||||
|
*
|
||||||
|
* The buffer that you pass must be double of MNL_SOCKET_BUFFER_SIZE. The
|
||||||
|
* limit must be half of the buffer size, otherwise expect funny memory
|
||||||
|
* corruptions 8-).
|
||||||
|
*
|
||||||
|
* You can allocate the buffer that you use to store the batch in the stack or
|
||||||
|
* the heap, no restrictions in this regard. This function returns NULL on
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
struct mnl_nlmsg_batch *mnl_nlmsg_batch_start(void *buf,
|
||||||
|
size_t limit)
|
||||||
|
{
|
||||||
|
struct mnl_nlmsg_batch *b;
|
||||||
|
|
||||||
|
b = malloc(sizeof(struct mnl_nlmsg_batch));
|
||||||
|
if (b == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
b->buf = buf;
|
||||||
|
b->limit = limit;
|
||||||
|
b->buflen = 0;
|
||||||
|
b->cur = buf;
|
||||||
|
b->overflow = false;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_stop - release a batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function releases the batch allocated by mnl_nlmsg_batch_start().
|
||||||
|
*/
|
||||||
|
void mnl_nlmsg_batch_stop(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
free(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_next - get room for the next message in the batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function returns false if the last message did not fit into the
|
||||||
|
* batch. Otherwise, it prepares the batch to provide room for the new
|
||||||
|
* Netlink message in the batch and returns true.
|
||||||
|
*
|
||||||
|
* You have to put at least one message in the batch before calling this
|
||||||
|
* function, otherwise your application is likely to crash.
|
||||||
|
*/
|
||||||
|
bool mnl_nlmsg_batch_next(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh = b->cur;
|
||||||
|
|
||||||
|
if (b->buflen + nlh->nlmsg_len > b->limit) {
|
||||||
|
b->overflow = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b->cur = b->buf + b->buflen + nlh->nlmsg_len;
|
||||||
|
b->buflen += nlh->nlmsg_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_reset - reset the batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function allows to reset a batch, so you can reuse it to create a
|
||||||
|
* new one. This function moves the last message which does not fit the
|
||||||
|
* batch to the head of the buffer, if any.
|
||||||
|
*/
|
||||||
|
void mnl_nlmsg_batch_reset(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
if (b->overflow) {
|
||||||
|
struct nlmsghdr *nlh = b->cur;
|
||||||
|
memcpy(b->buf, b->cur, nlh->nlmsg_len);
|
||||||
|
b->buflen = nlh->nlmsg_len;
|
||||||
|
b->cur = b->buf + b->buflen;
|
||||||
|
b->overflow = false;
|
||||||
|
} else {
|
||||||
|
b->buflen = 0;
|
||||||
|
b->cur = b->buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_size - get current size of the batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function returns the current size of the batch.
|
||||||
|
*/
|
||||||
|
size_t mnl_nlmsg_batch_size(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
return b->buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_head - get head of this batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the head of the batch, which is the
|
||||||
|
* beginning of the buffer that is used.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_batch_head(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
return b->buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_current - returns current position in the batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function returns a pointer to the current position in the buffer
|
||||||
|
* that is used to store the batch.
|
||||||
|
*/
|
||||||
|
void *mnl_nlmsg_batch_current(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
return b->cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_nlmsg_batch_is_empty - check if there is any message in the batch
|
||||||
|
* \param b pointer to batch
|
||||||
|
*
|
||||||
|
* This function returns true if the batch is empty.
|
||||||
|
*/
|
||||||
|
bool mnl_nlmsg_batch_is_empty(struct mnl_nlmsg_batch *b)
|
||||||
|
{
|
||||||
|
return b->buflen == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
351
package/wwan/app/fibocom-dial/src/libmnl/socket.c
Executable file
351
package/wwan/app/fibocom-dial/src/libmnl/socket.c
Executable file
@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2008-2010 by Pablo Neira Ayuso <pablo@netfilter.org>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "libmnl.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \mainpage
|
||||||
|
*
|
||||||
|
* libmnl is a minimalistic user-space library oriented to Netlink developers.
|
||||||
|
* There are a lot of common tasks in parsing, validating, constructing of
|
||||||
|
* both the Netlink header and TLVs that are repetitive and easy to get wrong.
|
||||||
|
* This library aims to provide simple helpers that allows you to avoid
|
||||||
|
* re-inventing the wheel in common Netlink tasks.
|
||||||
|
*
|
||||||
|
* \verbatim
|
||||||
|
"Simplify, simplify" -- Henry David Thoureau. Walden (1854)
|
||||||
|
\endverbatim
|
||||||
|
*
|
||||||
|
* The acronym libmnl stands for LIBrary Minimalistic NetLink.
|
||||||
|
*
|
||||||
|
* libmnl homepage is:
|
||||||
|
* http://www.netfilter.org/projects/libmnl/
|
||||||
|
*
|
||||||
|
* \section features Main Features
|
||||||
|
* - Small: the shared library requires around 30KB for an x86-based computer.
|
||||||
|
* - Simple: this library avoids complex abstractions that tend to hide Netlink
|
||||||
|
* details. It avoids elaborated object-oriented infrastructure and complex
|
||||||
|
* callback-based workflow.
|
||||||
|
* - Easy to use: the library simplifies the work for Netlink-wise developers.
|
||||||
|
* It provides functions to make socket handling, message building,
|
||||||
|
* validating, parsing and sequence tracking, easier.
|
||||||
|
* - Easy to re-use: you can use this library to build your own abstraction
|
||||||
|
* layer upon this library, if you want to provide another library that
|
||||||
|
* hides Netlink details to your users.
|
||||||
|
* - Decoupling: the interdependency of the main bricks that compose this
|
||||||
|
* library is reduced, i.e. the library provides many helpers, but the
|
||||||
|
* programmer is not forced to use them.
|
||||||
|
*
|
||||||
|
* \section licensing Licensing terms
|
||||||
|
* This library is released under the LGPLv2.1 or any later (at your option).
|
||||||
|
*
|
||||||
|
* \section Dependencies
|
||||||
|
* You have to install the Linux kernel headers that you want to use to develop
|
||||||
|
* your application. Moreover, this library requires that you have some basics
|
||||||
|
* on Netlink.
|
||||||
|
*
|
||||||
|
* \section scm Git Tree
|
||||||
|
* The current development version of libmnl can be accessed at:
|
||||||
|
* http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libmnl.git;a=summary
|
||||||
|
*
|
||||||
|
* \section using Using libmnl
|
||||||
|
* You can access several example files under examples/ in the libmnl source
|
||||||
|
* code tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct mnl_socket {
|
||||||
|
int fd;
|
||||||
|
struct sockaddr_nl addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup socket Netlink socket helpers
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_get_fd - obtain file descriptor from netlink socket
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
*
|
||||||
|
* This function returns the file descriptor of a given netlink socket.
|
||||||
|
*/
|
||||||
|
int mnl_socket_get_fd(const struct mnl_socket *nl)
|
||||||
|
{
|
||||||
|
return nl->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_get_portid - obtain Netlink PortID from netlink socket
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
*
|
||||||
|
* This function returns the Netlink PortID of a given netlink socket.
|
||||||
|
* It's a common mistake to assume that this PortID equals the process ID
|
||||||
|
* which is not always true. This is the case if you open more than one
|
||||||
|
* socket that is binded to the same Netlink subsystem from the same process.
|
||||||
|
*/
|
||||||
|
unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)
|
||||||
|
{
|
||||||
|
return nl->addr.nl_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mnl_socket *__mnl_socket_open(int bus, int flags)
|
||||||
|
{
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
|
||||||
|
nl = calloc(1, sizeof(struct mnl_socket));
|
||||||
|
if (nl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);
|
||||||
|
if (nl->fd == -1) {
|
||||||
|
free(nl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_open - open a netlink socket
|
||||||
|
* \param bus the netlink socket bus ID (see NETLINK_* constants)
|
||||||
|
*
|
||||||
|
* On error, it returns NULL and errno is appropriately set. Otherwise, it
|
||||||
|
* returns a valid pointer to the mnl_socket structure.
|
||||||
|
*/
|
||||||
|
struct mnl_socket *mnl_socket_open(int bus)
|
||||||
|
{
|
||||||
|
return __mnl_socket_open(bus, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_open2 - open a netlink socket with appropriate flags
|
||||||
|
* \param bus the netlink socket bus ID (see NETLINK_* constants)
|
||||||
|
* \param flags the netlink socket flags (see SOCK_* constants in socket(2))
|
||||||
|
*
|
||||||
|
* This is similar to mnl_socket_open(), but allows to set flags like
|
||||||
|
* SOCK_CLOEXEC at socket creation time (useful for multi-threaded programs
|
||||||
|
* performing exec calls).
|
||||||
|
*
|
||||||
|
* On error, it returns NULL and errno is appropriately set. Otherwise, it
|
||||||
|
* returns a valid pointer to the mnl_socket structure.
|
||||||
|
*/
|
||||||
|
struct mnl_socket *mnl_socket_open2(int bus, int flags)
|
||||||
|
{
|
||||||
|
return __mnl_socket_open(bus, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_fdopen - associates a mnl_socket object with pre-existing socket.
|
||||||
|
* \param fd pre-existing socket descriptor.
|
||||||
|
*
|
||||||
|
* On error, it returns NULL and errno is appropriately set. Otherwise, it
|
||||||
|
* returns a valid pointer to the mnl_socket structure. It also sets the portID
|
||||||
|
* if the socket fd is already bound and it is AF_NETLINK.
|
||||||
|
*
|
||||||
|
* Note that mnl_socket_get_portid() returns 0 if this function is used with
|
||||||
|
* non-netlink socket.
|
||||||
|
*/
|
||||||
|
struct mnl_socket *mnl_socket_fdopen(int fd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct mnl_socket *nl;
|
||||||
|
struct sockaddr_nl addr;
|
||||||
|
socklen_t addr_len = sizeof(struct sockaddr_nl);
|
||||||
|
|
||||||
|
ret = getsockname(fd, (struct sockaddr *) &addr, &addr_len);
|
||||||
|
if (ret == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nl = calloc(1, sizeof(struct mnl_socket));
|
||||||
|
if (nl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nl->fd = fd;
|
||||||
|
if (addr.nl_family == AF_NETLINK)
|
||||||
|
nl->addr = addr;
|
||||||
|
|
||||||
|
return nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_bind - bind netlink socket
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
* \param groups the group of message you're interested in
|
||||||
|
* \param pid the port ID you want to use (use zero for automatic selection)
|
||||||
|
*
|
||||||
|
* On error, this function returns -1 and errno is appropriately set. On
|
||||||
|
* success, 0 is returned. You can use MNL_SOCKET_AUTOPID which is 0 for
|
||||||
|
* automatic port ID selection.
|
||||||
|
*/
|
||||||
|
int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups,
|
||||||
|
pid_t pid)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
socklen_t addr_len;
|
||||||
|
|
||||||
|
nl->addr.nl_family = AF_NETLINK;
|
||||||
|
nl->addr.nl_groups = groups;
|
||||||
|
nl->addr.nl_pid = pid;
|
||||||
|
|
||||||
|
ret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
addr_len = sizeof(nl->addr);
|
||||||
|
ret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (addr_len != sizeof(nl->addr)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (nl->addr.nl_family != AF_NETLINK) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_sendto - send a netlink message of a certain size
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
* \param buf buffer containing the netlink message to be sent
|
||||||
|
* \param len number of bytes in the buffer that you want to send
|
||||||
|
*
|
||||||
|
* On error, it returns -1 and errno is appropriately set. Otherwise, it
|
||||||
|
* returns the number of bytes sent.
|
||||||
|
*/
|
||||||
|
ssize_t mnl_socket_sendto(const struct mnl_socket *nl,
|
||||||
|
const void *buf, size_t len)
|
||||||
|
{
|
||||||
|
static const struct sockaddr_nl snl = {
|
||||||
|
.nl_family = AF_NETLINK
|
||||||
|
};
|
||||||
|
return sendto(nl->fd, buf, len, 0,
|
||||||
|
(struct sockaddr *) &snl, sizeof(snl));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_recvfrom - receive a netlink message
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
* \param buf buffer that you want to use to store the netlink message
|
||||||
|
* \param bufsiz size of the buffer passed to store the netlink message
|
||||||
|
*
|
||||||
|
* On error, it returns -1 and errno is appropriately set. If errno is set
|
||||||
|
* to ENOSPC, it means that the buffer that you have passed to store the
|
||||||
|
* netlink message is too small, so you have received a truncated message.
|
||||||
|
* To avoid this, you have to allocate a buffer of MNL_SOCKET_BUFFER_SIZE
|
||||||
|
* (which is 8KB, see linux/netlink.h for more information). Using this
|
||||||
|
* buffer size ensures that your buffer is big enough to store the netlink
|
||||||
|
* message without truncating it.
|
||||||
|
*/
|
||||||
|
ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl,
|
||||||
|
void *buf, size_t bufsiz)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
struct sockaddr_nl addr;
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = buf,
|
||||||
|
.iov_len = bufsiz,
|
||||||
|
};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = &addr,
|
||||||
|
.msg_namelen = sizeof(struct sockaddr_nl),
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = NULL,
|
||||||
|
.msg_controllen = 0,
|
||||||
|
.msg_flags = 0,
|
||||||
|
};
|
||||||
|
ret = recvmsg(nl->fd, &msg, 0);
|
||||||
|
if (ret == -1)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (msg.msg_flags & MSG_TRUNC) {
|
||||||
|
errno = ENOSPC;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_close - close a given netlink socket
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
*
|
||||||
|
* On error, this function returns -1 and errno is appropriately set.
|
||||||
|
* On success, it returns 0.
|
||||||
|
*/
|
||||||
|
int mnl_socket_close(struct mnl_socket *nl)
|
||||||
|
{
|
||||||
|
int ret = close(nl->fd);
|
||||||
|
free(nl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_setsockopt - set Netlink socket option
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
* \param type type of Netlink socket options
|
||||||
|
* \param buf the buffer that contains the data about this option
|
||||||
|
* \param len the size of the buffer passed
|
||||||
|
*
|
||||||
|
* This function allows you to set some Netlink socket option. As of writing
|
||||||
|
* this (see linux/netlink.h), the existing options are:
|
||||||
|
*
|
||||||
|
* - \#define NETLINK_ADD_MEMBERSHIP 1
|
||||||
|
* - \#define NETLINK_DROP_MEMBERSHIP 2
|
||||||
|
* - \#define NETLINK_PKTINFO 3
|
||||||
|
* - \#define NETLINK_BROADCAST_ERROR 4
|
||||||
|
* - \#define NETLINK_NO_ENOBUFS 5
|
||||||
|
*
|
||||||
|
* In the early days, Netlink only supported 32 groups expressed in a
|
||||||
|
* 32-bits mask. However, since 2.6.14, Netlink may have up to 2^32 multicast
|
||||||
|
* groups but you have to use setsockopt() with NETLINK_ADD_MEMBERSHIP to
|
||||||
|
* join a given multicast group. This function internally calls setsockopt()
|
||||||
|
* to join a given netlink multicast group. You can still use mnl_bind()
|
||||||
|
* and the 32-bit mask to join a set of Netlink multicast groups.
|
||||||
|
*
|
||||||
|
* On error, this function returns -1 and errno is appropriately set.
|
||||||
|
*/
|
||||||
|
int mnl_socket_setsockopt(const struct mnl_socket *nl, int type,
|
||||||
|
void *buf, socklen_t len)
|
||||||
|
{
|
||||||
|
return setsockopt(nl->fd, SOL_NETLINK, type, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mnl_socket_getsockopt - get a Netlink socket option
|
||||||
|
* \param nl netlink socket obtained via mnl_socket_open()
|
||||||
|
* \param type type of Netlink socket options
|
||||||
|
* \param buf pointer to the buffer to store the value of this option
|
||||||
|
* \param len size of the information written in the buffer
|
||||||
|
*
|
||||||
|
* On error, this function returns -1 and errno is appropriately set.
|
||||||
|
*/
|
||||||
|
int mnl_socket_getsockopt(const struct mnl_socket *nl, int type,
|
||||||
|
void *buf, socklen_t *len)
|
||||||
|
{
|
||||||
|
return getsockopt(nl->fd, SOL_NETLINK, type, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
1770
package/wwan/app/fibocom-dial/src/main.c
Executable file
1770
package/wwan/app/fibocom-dial/src/main.c
Executable file
File diff suppressed because it is too large
Load Diff
1019
package/wwan/app/fibocom-dial/src/multi-pdn-manager.c
Executable file
1019
package/wwan/app/fibocom-dial/src/multi-pdn-manager.c
Executable file
File diff suppressed because it is too large
Load Diff
67
package/wwan/app/fibocom-dial/src/multi-pdn.ini
Executable file
67
package/wwan/app/fibocom-dial/src/multi-pdn.ini
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
[Setting]
|
||||||
|
qmap_num=4
|
||||||
|
|
||||||
|
[profile1]
|
||||||
|
apn=1234567890
|
||||||
|
user=qwertyuio
|
||||||
|
password=asdfghjkl
|
||||||
|
auth=2
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile2]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=0
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile3]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=0
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile4]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=0
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile5]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile6]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile7]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
||||||
|
[profile8]
|
||||||
|
apn=ctnet
|
||||||
|
user=
|
||||||
|
password=
|
||||||
|
auth=
|
||||||
|
ipfamily=1
|
||||||
|
pin=
|
||||||
|
|
438
package/wwan/app/fibocom-dial/src/qmap_bridge_mode.c
Executable file
438
package/wwan/app/fibocom-dial/src/qmap_bridge_mode.c
Executable file
@ -0,0 +1,438 @@
|
|||||||
|
#include "QMIThread.h"
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
|
||||||
|
#include <net/if.h>
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
|
||||||
|
|
||||||
|
static size_t fibo_fread(const char *filename, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
memset(buf, 0x00, size);
|
||||||
|
|
||||||
|
if (fp) {
|
||||||
|
n = fread(buf, 1, size, fp);
|
||||||
|
if (n <= 0 || n == size) {
|
||||||
|
dbg_time(
|
||||||
|
"warnning: fail to fread(%s), fread=%zd, buf_size=%zd, "
|
||||||
|
"errno: %d (%s)",
|
||||||
|
__func__, filename, n, size, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n > 0 ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t fibo_fwrite(const char *filename, const void *buf, size_t size)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(filename, "w");
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
if (fp) {
|
||||||
|
n = fwrite(buf, 1, size, fp);
|
||||||
|
if (n != size) {
|
||||||
|
dbg_time(
|
||||||
|
"warnning: fail to fwrite(%s), fwrite=%zd, buf_size=%zd, "
|
||||||
|
"errno: %d (%s)",
|
||||||
|
__func__, filename, n, size, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return n > 0 ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fibo_iface_is_in_bridge(const char *iface)
|
||||||
|
{
|
||||||
|
char filename[256];
|
||||||
|
|
||||||
|
snprintf(filename, sizeof(filename), "/sys/class/net/%s/brport", iface);
|
||||||
|
return (access(filename, F_OK) == 0 || errno != ENOENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fibo_bridge_mode_detect(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
const char *ifname = profile->qmapnet_adapter ? profile->qmapnet_adapter
|
||||||
|
: profile->usbnet_adapter;
|
||||||
|
const char *driver;
|
||||||
|
char bridge_mode[128];
|
||||||
|
char bridge_ipv4[128];
|
||||||
|
char ipv4[128];
|
||||||
|
char buf[64];
|
||||||
|
size_t n;
|
||||||
|
int in_bridge;
|
||||||
|
|
||||||
|
driver = profile->driver_name;
|
||||||
|
snprintf(bridge_mode, sizeof(bridge_mode), "/sys/class/net/%s/bridge_mode",
|
||||||
|
ifname);
|
||||||
|
snprintf(bridge_ipv4, sizeof(bridge_ipv4), "/sys/class/net/%s/bridge_ipv4",
|
||||||
|
ifname);
|
||||||
|
|
||||||
|
if (access(bridge_mode, F_OK) && errno == ENOENT) {
|
||||||
|
snprintf(bridge_mode, sizeof(bridge_mode),
|
||||||
|
"/sys/module/%s/parameters/bridge_mode", driver);
|
||||||
|
snprintf(bridge_ipv4, sizeof(bridge_ipv4),
|
||||||
|
"/sys/module/%s/parameters/bridge_ipv4", driver);
|
||||||
|
|
||||||
|
if (access(bridge_mode, F_OK) && errno == ENOENT) {
|
||||||
|
bridge_mode[0] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in_bridge = fibo_iface_is_in_bridge(ifname);
|
||||||
|
if (in_bridge) {
|
||||||
|
dbg_time("notice: iface %s had add to bridge\n", ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_bridge && bridge_mode[0] == '\0') {
|
||||||
|
dbg_time("warnning: can not find bride_mode file for %s\n", ifname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = fibo_fread(bridge_mode, buf, sizeof(buf));
|
||||||
|
|
||||||
|
if (in_bridge) {
|
||||||
|
if (n <= 0 || buf[0] == '0') {
|
||||||
|
dbg_time("warnning: should set 1 to bride_mode file for %s\n",
|
||||||
|
ifname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n <= 0 || buf[0] == '0') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ipv4, 0, sizeof(ipv4));
|
||||||
|
|
||||||
|
if (strstr(bridge_ipv4, "/sys/class/net/") || profile->qmap_mode == 0 ||
|
||||||
|
profile->qmap_mode == 1) {
|
||||||
|
snprintf(ipv4, sizeof(ipv4), "0x%x", profile->ipv4.Address);
|
||||||
|
dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
|
||||||
|
fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
|
||||||
|
} else {
|
||||||
|
snprintf(ipv4, sizeof(ipv4), "0x%x:%d", profile->ipv4.Address,
|
||||||
|
profile->muxid);
|
||||||
|
dbg_time("echo '%s' > %s", ipv4, bridge_ipv4);
|
||||||
|
fibo_fwrite(bridge_ipv4, ipv4, strlen(ipv4));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
|
||||||
|
void fibo_get_driver_info(PROFILE_T *profile, RMNET_INFO *rmnet_info) {
|
||||||
|
int ifc_ctl_sock;
|
||||||
|
struct ifreq ifr;
|
||||||
|
int rc;
|
||||||
|
int request = 0x89F3;
|
||||||
|
unsigned char data[512];
|
||||||
|
|
||||||
|
memset(rmnet_info, 0x00, sizeof(*rmnet_info));
|
||||||
|
|
||||||
|
ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (ifc_ctl_sock <= 0) {
|
||||||
|
dbg_time("socket() failed: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ifr, 0, sizeof(struct ifreq));
|
||||||
|
strncpy(ifr.ifr_name, profile->usbnet_adapter, IFNAMSIZ);
|
||||||
|
ifr.ifr_name[IFNAMSIZ - 1] = 0;
|
||||||
|
ifr.ifr_ifru.ifru_data = (void *)data;
|
||||||
|
rc = ioctl(ifc_ctl_sock, request, &ifr);
|
||||||
|
if (rc < 0) {
|
||||||
|
dbg_time("ioctl(0x%x, qmap_settings) failed: %s, rc=%d", request, strerror(errno), rc);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(rmnet_info, data, sizeof(*rmnet_info));
|
||||||
|
}
|
||||||
|
|
||||||
|
close(ifc_ctl_sock);
|
||||||
|
}
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
|
||||||
|
int fibo_qmap_mode_detect(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char buf[128];
|
||||||
|
char qmap_netcard[128];
|
||||||
|
struct {
|
||||||
|
char filename[255 * 2];
|
||||||
|
char linkname[255 * 2];
|
||||||
|
} * pl;
|
||||||
|
|
||||||
|
pl = (typeof(pl))malloc(sizeof(*pl));
|
||||||
|
|
||||||
|
snprintf(pl->linkname, sizeof(pl->linkname),
|
||||||
|
"/sys/class/net/%s/device/driver", profile->usbnet_adapter);
|
||||||
|
n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
|
||||||
|
pl->filename[n] = '\0';
|
||||||
|
while (pl->filename[n] != '/') n--;
|
||||||
|
profile->driver_name = strdup(&pl->filename[n + 1]);
|
||||||
|
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed begin for oa 20210311037
|
||||||
|
fibo_get_driver_info(profile, &profile->rmnet_info);
|
||||||
|
if (profile->rmnet_info.size) {
|
||||||
|
profile->qmap_mode = profile->rmnet_info.qmap_mode;
|
||||||
|
if (profile->qmap_mode) {
|
||||||
|
int offset_id = profile->pdp - 1;
|
||||||
|
|
||||||
|
if (profile->qmap_mode == 1)
|
||||||
|
offset_id = 0;
|
||||||
|
profile->muxid = profile->rmnet_info.mux_id[offset_id];
|
||||||
|
profile->qmapnet_adapter = strdup( profile->rmnet_info.ifname[offset_id]);
|
||||||
|
profile->qmap_size = profile->rmnet_info.rx_urb_size;
|
||||||
|
profile->qmap_version = profile->rmnet_info.qmap_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
//2021-03-15 zhangkaibo@fibocom.com changed end for oa 20210311037
|
||||||
|
|
||||||
|
if (qmidev_is_pciemhi(profile->qmichannel)) {
|
||||||
|
profile->qmap_mode = 1;
|
||||||
|
|
||||||
|
if (profile->muxid == 0 || profile->muxid == 0x81)
|
||||||
|
{
|
||||||
|
profile->muxid = 0x81;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (profile->muxid < 0x80)
|
||||||
|
profile->muxid += 0x81;
|
||||||
|
profile->qmap_mode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
|
||||||
|
goto _final_process;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
|
||||||
|
//2021-01-27 willa.liu@fibocom.com changed begin for support mantis 0068849
|
||||||
|
if (access(pl->filename, F_OK) == 0) {
|
||||||
|
dbg_time("access %s", pl->filename);
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
|
||||||
|
strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
|
||||||
|
/*
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/module/%s/parameters/qmap_num", profile->driver_name);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
|
||||||
|
errno, strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
|
||||||
|
errno, strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//2021-02-01 willa.liu@fibocom.com changed end for support mantis 0069837
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_mode",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
|
||||||
|
if (access(pl->filename, F_OK) == 0) {
|
||||||
|
dbg_time("access %s", pl->filename);
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
|
||||||
|
strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!access(pl->filename, R_OK))
|
||||||
|
{
|
||||||
|
n = fibo_fread(pl->filename, buf, sizeof(buf));
|
||||||
|
if(n > 0)
|
||||||
|
{
|
||||||
|
profile->qmap_mode = atoi(buf);
|
||||||
|
if(profile->qmap_mode >= 1 && qmidev_is_pciemhi(profile->qmichannel))
|
||||||
|
{
|
||||||
|
profile->muxid =
|
||||||
|
profile->pdp + 0x80;
|
||||||
|
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
}
|
||||||
|
if(qmidev_is_gobinet(profile->qmichannel) || qmidev_is_qmiwwan(profile->qmichannel))
|
||||||
|
{
|
||||||
|
if(profile->qmap_mode > 1)
|
||||||
|
{
|
||||||
|
profile->muxid =
|
||||||
|
profile->pdp + 0x80;
|
||||||
|
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
}
|
||||||
|
if(profile->qmap_mode == 1)
|
||||||
|
{
|
||||||
|
profile->muxid = 0x81;
|
||||||
|
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
profile->qmap_mode = atoi(buf);
|
||||||
|
|
||||||
|
if (profile->qmap_mode > 1 && qmidev_is_gobinet(profile->qmichannel)) {
|
||||||
|
profile->muxid =
|
||||||
|
profile->pdp + 0x80; // muxis is 0x8X for PDN-X
|
||||||
|
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
}
|
||||||
|
if (profile->qmap_mode >= 1 && !qmidev_is_gobinet(profile->qmichannel)) {
|
||||||
|
profile->muxid =
|
||||||
|
profile->pdp + 0x80; // muxis is 0x8X for PDN-X
|
||||||
|
sprintf(qmap_netcard, "%s.%d", profile->usbnet_adapter,profile->pdp);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
}
|
||||||
|
if (profile->qmap_mode == 1 && qmidev_is_gobinet(profile->qmichannel)) {
|
||||||
|
profile->muxid = 0x81;
|
||||||
|
profile->qmapnet_adapter = strdup(profile->usbnet_adapter);
|
||||||
|
//2021-01-27 willa.liu@fibocom.com changed end for support mantis 0068849
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (qmidev_is_qmiwwan(profile->qmichannel)) {
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/qmimux%d",
|
||||||
|
profile->pdp - 1);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
|
||||||
|
errno, strerror(errno));
|
||||||
|
}
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// upstream Kernel Style QMAP qmi_wwan.c
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/class/net/%s/qmi/add_mux", profile->usbnet_adapter);
|
||||||
|
n = fibo_fread(pl->filename, buf, sizeof(buf));
|
||||||
|
if (n >= 5) {
|
||||||
|
profile->qmap_mode = n / 5; // 0x81\n0x82\n0x83\n
|
||||||
|
if (profile->qmap_mode > 1) {
|
||||||
|
// PDN-X map to qmimux-X
|
||||||
|
profile->muxid = (buf[5 * (profile->pdp - 1) + 2] - '0') * 16 +
|
||||||
|
(buf[5 * (profile->pdp - 1) + 3] - '0');
|
||||||
|
sprintf(qmap_netcard, "qmimux%d", profile->pdp - 1);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
} else if (profile->qmap_mode == 1) {
|
||||||
|
profile->muxid =
|
||||||
|
(buf[5 * 0 + 2] - '0') * 16 + (buf[5 * 0 + 3] - '0');
|
||||||
|
sprintf(qmap_netcard, "qmimux%d", 0);
|
||||||
|
profile->qmapnet_adapter = strdup(qmap_netcard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_out:
|
||||||
|
if (profile->qmap_mode) {
|
||||||
|
profile->qmap_size = 16 * 1024;
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/class/net/%s/qmap_size", profile->usbnet_adapter);
|
||||||
|
if (!access(pl->filename, R_OK)) {
|
||||||
|
size_t n;
|
||||||
|
char buf[32];
|
||||||
|
n = fibo_fread(pl->filename, buf, sizeof(buf));
|
||||||
|
if (n > 0) {
|
||||||
|
profile->qmap_size = atoi(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_final_process:
|
||||||
|
if (profile->qmap_mode)
|
||||||
|
dbg_time("qmap_mode = %d, muxid = 0x%02x, qmap_netcard = %s",
|
||||||
|
profile->qmap_mode, profile->muxid, profile->qmapnet_adapter);
|
||||||
|
|
||||||
|
free(pl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fibo_qmap_mode_set(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
char buf[128];
|
||||||
|
struct {
|
||||||
|
char filename[255 * 2];
|
||||||
|
char linkname[255 * 2];
|
||||||
|
} * pl;
|
||||||
|
|
||||||
|
if (qmidev_is_pciemhi(profile->qmichannel))
|
||||||
|
{
|
||||||
|
dbg_time("pcie mode exit fibo_qmap_mode_set ");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pl = (typeof(pl))malloc(sizeof(*pl));
|
||||||
|
|
||||||
|
snprintf(pl->linkname, sizeof(pl->linkname),
|
||||||
|
"/sys/class/net/%s/device/driver", profile->usbnet_adapter);
|
||||||
|
n = readlink(pl->linkname, pl->filename, sizeof(pl->filename));
|
||||||
|
pl->filename[n] = '\0';
|
||||||
|
while (pl->filename[n] != '/') n--;
|
||||||
|
profile->driver_name = strdup(&pl->filename[n + 1]);
|
||||||
|
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename), "/sys/class/net/%s/qmap_num",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename, errno,
|
||||||
|
strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/module/%s/parameters/qmap_mnum", profile->driver_name);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
|
||||||
|
errno, strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(
|
||||||
|
pl->filename, sizeof(pl->filename),
|
||||||
|
"/sys/class/net/%s/device/driver/module/parameters/qmap_num",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
if (access(pl->filename, R_OK)) {
|
||||||
|
if (errno != ENOENT) {
|
||||||
|
dbg_time("fail to access %s, errno: %d (%s)", pl->filename,
|
||||||
|
errno, strerror(errno));
|
||||||
|
goto _out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!access(pl->filename, R_OK)) {
|
||||||
|
snprintf(buf, sizeof(buf), "%d", profile->pdpnum);
|
||||||
|
n = fibo_fwrite(pl->filename, buf, strlen(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
_out:
|
||||||
|
|
||||||
|
free(pl);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
160
package/wwan/app/fibocom-dial/src/query_pcie_mode.c
Executable file
160
package/wwan/app/fibocom-dial/src/query_pcie_mode.c
Executable file
@ -0,0 +1,160 @@
|
|||||||
|
//2021-03-24 willa.liu@fibocom.com changed begin for support mantis 0071817
|
||||||
|
#include "QMIThread.h"
|
||||||
|
#include "query_pcie_mode.h"
|
||||||
|
int speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300,//
|
||||||
|
B38400, B19200, B9600, B4800, B2400, B1200, B300,
|
||||||
|
};
|
||||||
|
int name_arr[] = {115200, 19200, 9600, 4800, 2400, 1200, 300,
|
||||||
|
38400, 19200, 9600, 4800, 2400, 1200, 300,
|
||||||
|
};
|
||||||
|
|
||||||
|
int get_pcie_mode_debug = 0;
|
||||||
|
|
||||||
|
//2021-02-24 willa.liu@fibocom.com changed begin for support eipd SN-20210129001
|
||||||
|
int get_private_gateway_debug = 1;
|
||||||
|
//2021-02-24 willa.liu@fibocom.com changed end for support eipd SN-20210129001
|
||||||
|
|
||||||
|
//static int xset1(int fd, struct termios *tio, const char *device)
|
||||||
|
int xset1(int fd, struct termios *tio, const char *device)
|
||||||
|
{
|
||||||
|
int ret = tcsetattr(fd, TCSAFLUSH, tio);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
printf("can't tcsetattr for %s", device);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set raw tty mode
|
||||||
|
//static void xget1(int fd, struct termios *t, struct termios *oldt)
|
||||||
|
void xget1(int fd, struct termios *t, struct termios *oldt)
|
||||||
|
{
|
||||||
|
tcgetattr(fd, oldt);
|
||||||
|
*t = *oldt;
|
||||||
|
cfmakeraw(t);
|
||||||
|
// t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
|
||||||
|
// t->c_iflag &= ~(BRKINT|IXON|ICRNL);
|
||||||
|
// t->c_oflag &= ~(ONLCR);
|
||||||
|
// t->c_cc[VMIN] = 1;
|
||||||
|
// t->c_cc[VTIME] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//static int get_pcie_mode()
|
||||||
|
int get_pcie_mode()
|
||||||
|
//2021-03-24 willa.liu@fibocom.com changed end for support mantis 0071817
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int fd;
|
||||||
|
int ret;
|
||||||
|
char buffer[409600] = {0};
|
||||||
|
int rate;
|
||||||
|
char* sendbuffer;
|
||||||
|
int totallen = 0;
|
||||||
|
fd_set readfds;
|
||||||
|
struct timeval timeout;
|
||||||
|
struct termios tio0, tiosfd, tio;
|
||||||
|
|
||||||
|
int timeoutVal = 5;
|
||||||
|
|
||||||
|
char *dev = "/dev/ttyUSB1"; //The port under Linux is operated by opening the device file
|
||||||
|
rate = 115200;
|
||||||
|
sendbuffer = "at+gtpcie=3";
|
||||||
|
printf ( "dev: %s\nrate:%d\nsendbuffer:%s\n", dev,rate,sendbuffer);
|
||||||
|
|
||||||
|
fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK);
|
||||||
|
if (fd < 0)
|
||||||
|
goto ERR;
|
||||||
|
fcntl(fd, F_SETFL, O_RDWR);
|
||||||
|
|
||||||
|
// put device to "raw mode"
|
||||||
|
xget1(fd, &tio, &tiosfd);
|
||||||
|
// set device speed
|
||||||
|
for ( i = 0;i < sizeof ( speed_arr ) / sizeof ( int );i++ )
|
||||||
|
{
|
||||||
|
if ( rate == name_arr[i] ) //Judge if the pass is equal to the pass
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i >= sizeof ( speed_arr ) / sizeof ( int ))
|
||||||
|
{
|
||||||
|
printf("bound rate set failed\n");
|
||||||
|
goto ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfsetspeed(&tio, speed_arr[i]);
|
||||||
|
if (xset1(fd, &tio, dev))
|
||||||
|
goto ERR;
|
||||||
|
|
||||||
|
tcflush ( fd, TCIOFLUSH );
|
||||||
|
|
||||||
|
sprintf(buffer, "%s\r", sendbuffer);
|
||||||
|
ret = write ( fd, buffer, strlen(buffer) );
|
||||||
|
if(ret < 0)
|
||||||
|
{
|
||||||
|
printf ( "write failed\n" );
|
||||||
|
goto ERR;
|
||||||
|
}
|
||||||
|
if(get_pcie_mode_debug)printf("write %d\n", ret);
|
||||||
|
|
||||||
|
FD_ZERO ( &readfds );
|
||||||
|
FD_SET ( fd, &readfds );
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
timeout.tv_sec = timeoutVal;
|
||||||
|
timeout.tv_usec = 0;
|
||||||
|
|
||||||
|
ret = select ( fd+1, &readfds, ( fd_set * ) 0, ( fd_set * ) 0, &timeout );
|
||||||
|
if(ret > 0)
|
||||||
|
{
|
||||||
|
ret = read ( fd, buffer+totallen, sizeof(buffer)-totallen-1 );
|
||||||
|
if(ret < 0)
|
||||||
|
{
|
||||||
|
printf ( "read failed\n" );
|
||||||
|
goto ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ret == 0)
|
||||||
|
{
|
||||||
|
goto ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
totallen += ret;
|
||||||
|
buffer[totallen] = '\0';
|
||||||
|
if(get_pcie_mode_debug)printf("read %d(%s)\n", ret, &buffer[totallen-ret]);
|
||||||
|
if(totallen == sizeof(buffer)-1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(strstr(buffer,"\nOK") || strstr(buffer,"\nERROR")
|
||||||
|
|| strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:"))
|
||||||
|
{
|
||||||
|
if(get_pcie_mode_debug)printf("match OK/ERROR");
|
||||||
|
if(get_pcie_mode_debug)printf("%s", buffer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf ( "select timeout\n" );
|
||||||
|
goto ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tcsetattr(fd, TCSAFLUSH, &tiosfd);
|
||||||
|
//printf("buffer:\n %s\n", buffer);
|
||||||
|
printf("%s\n", buffer);
|
||||||
|
if(strstr(buffer,"\nERROR") || strstr(buffer,"\n+CME ERROR:") || strstr(buffer,"\n+CMS ERROR:"))
|
||||||
|
goto ERR;
|
||||||
|
|
||||||
|
close ( fd );
|
||||||
|
return 0;
|
||||||
|
ERR:
|
||||||
|
if(fd > 0)
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
17
package/wwan/app/fibocom-dial/src/query_pcie_mode.h
Executable file
17
package/wwan/app/fibocom-dial/src/query_pcie_mode.h
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef __QUERY_PCIE_MODE__
|
||||||
|
#define __QUERY_PCIE_MODE__
|
||||||
|
|
||||||
|
extern int speed_arr[14];
|
||||||
|
extern int name_arr[14];
|
||||||
|
|
||||||
|
extern int get_pcie_mode_debug;
|
||||||
|
|
||||||
|
extern int get_private_gateway_debug;
|
||||||
|
|
||||||
|
extern int xset1(int fd, struct termios *tio, const char *device);
|
||||||
|
|
||||||
|
// set raw tty mode
|
||||||
|
extern void xget1(int fd, struct termios *t, struct termios *oldt);
|
||||||
|
extern int get_pcie_mode();
|
||||||
|
|
||||||
|
#endif
|
289
package/wwan/app/fibocom-dial/src/udhcpc.c
Executable file
289
package/wwan/app/fibocom-dial/src/udhcpc.c
Executable file
@ -0,0 +1,289 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <endian.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "QMIThread.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static int dibbler_client_alive = 0;
|
||||||
|
|
||||||
|
static int fibo_system(const char *shell_cmd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
dbg_time("%s", shell_cmd);
|
||||||
|
ret = system(shell_cmd);
|
||||||
|
if (ret) {
|
||||||
|
// dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd,
|
||||||
|
// ret, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fibo_set_mtu(const char *ifname, int ifru_mtu)
|
||||||
|
{
|
||||||
|
int inet_sock;
|
||||||
|
struct ifreq ifr;
|
||||||
|
|
||||||
|
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
if (inet_sock > 0) {
|
||||||
|
strcpy(ifr.ifr_name, ifname);
|
||||||
|
|
||||||
|
if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) {
|
||||||
|
if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) {
|
||||||
|
dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu,
|
||||||
|
ifru_mtu);
|
||||||
|
ifr.ifr_ifru.ifru_mtu = ifru_mtu;
|
||||||
|
ioctl(inet_sock, SIOCSIFMTU, &ifr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(inet_sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *udhcpc_thread_function(void *arg)
|
||||||
|
{
|
||||||
|
FILE *udhcpc_fp;
|
||||||
|
char *udhcpc_cmd = (char *)arg;
|
||||||
|
|
||||||
|
if (udhcpc_cmd == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dbg_time("%s", udhcpc_cmd);
|
||||||
|
udhcpc_fp = popen(udhcpc_cmd, "r");
|
||||||
|
free(udhcpc_cmd);
|
||||||
|
if (udhcpc_fp) {
|
||||||
|
char buf[0xff];
|
||||||
|
|
||||||
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
while ((fgets(buf, sizeof(buf) - 1, udhcpc_fp)) != NULL) {
|
||||||
|
if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n'))
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
dbg_time("%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(udhcpc_fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fibo_set_driver_link_state(PROFILE_T *profile, int link_state)
|
||||||
|
{
|
||||||
|
char link_file[128];
|
||||||
|
int fd;
|
||||||
|
int new_state = 0;
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state",
|
||||||
|
profile->usbnet_adapter);
|
||||||
|
fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
if (fd == -1) {
|
||||||
|
if (errno != ENOENT)
|
||||||
|
dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno,
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->qmap_mode <= 1)
|
||||||
|
new_state = !!link_state;
|
||||||
|
else {
|
||||||
|
// 0x80 means link off this pdp
|
||||||
|
new_state = (link_state ? 0x00 : 0x80) + profile->pdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(link_file, sizeof(link_file), "%d\n", new_state);
|
||||||
|
write(fd, link_file, sizeof(link_file));
|
||||||
|
|
||||||
|
if (link_state == 0 && profile->qmap_mode > 1) {
|
||||||
|
size_t rc;
|
||||||
|
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
rc = read(fd, link_file, sizeof(link_file));
|
||||||
|
if (rc > 1 && (!strcasecmp(link_file, "0\n") ||
|
||||||
|
!strcasecmp(link_file, "0x0\n"))) {
|
||||||
|
// snprintf(link_file, sizeof(link_file), "busybox ifconfig %s down",
|
||||||
|
// profile->usbnet_adapter);
|
||||||
|
// fibo_system(link_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fibo_raw_ip_mode_check(const char *ifname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char raw_ip[128];
|
||||||
|
char shell_cmd[128];
|
||||||
|
char mode[2] = "X";
|
||||||
|
int mode_change = 0;
|
||||||
|
|
||||||
|
snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname);
|
||||||
|
if (access(raw_ip, F_OK))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
if (fd < 0) {
|
||||||
|
dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__,
|
||||||
|
raw_ip, errno, strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
read(fd, mode, 2);
|
||||||
|
if (mode[0] == '0' || mode[0] == 'N') {
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s down", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname);
|
||||||
|
mode[0] = 'Y';
|
||||||
|
write(fd, mode, 2);
|
||||||
|
mode_change = 1;
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd), "busybox ifconfig %s up", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return mode_change;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_start(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
char shell_cmd[128];
|
||||||
|
|
||||||
|
fibo_set_driver_link_state(profile, 1);
|
||||||
|
if (profile->qmapnet_adapter) {
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
dbg_time("1 %s", profile->qmichannel);
|
||||||
|
if (qmidev_is_qmiwwan(profile->qmichannel)) {
|
||||||
|
dbg_time("2 %s", ifname);
|
||||||
|
fibo_raw_ip_mode_check(ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) {
|
||||||
|
fibo_set_mtu(ifname, (profile->ipv4.Mtu));
|
||||||
|
}
|
||||||
|
|
||||||
|
fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check");
|
||||||
|
|
||||||
|
//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605
|
||||||
|
// if (strcmp(ifname, profile->usbnet_adapter)) {
|
||||||
|
// snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up",
|
||||||
|
// profile->usbnet_adapter);
|
||||||
|
// fibo_system(shell_cmd);
|
||||||
|
// }
|
||||||
|
//begin modified by zhangkaibo fix ipv6 dial process flow. mantis 0048789 20200605
|
||||||
|
// For IPv6, down & up will send protocol packets, and that's needed.
|
||||||
|
if (profile->ipv6_flag) {
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s up", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
//begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s arp off multicast on", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
//begin modified by zhangming Added NOARP and Multilcast on flag bit commands. mantis 0050106 20200713
|
||||||
|
//Modified unicom dual stack dialing unsuccessful problem
|
||||||
|
|
||||||
|
// for bridge mode, only one public IP, so do udhcpc manually
|
||||||
|
if (fibo_bridge_mode_detect(profile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do DHCP using busybox tools */
|
||||||
|
{
|
||||||
|
char udhcpc_cmd[128];
|
||||||
|
pthread_attr_t udhcpc_thread_attr;
|
||||||
|
pthread_t udhcpc_thread_id;
|
||||||
|
|
||||||
|
pthread_attr_init(&udhcpc_thread_attr);
|
||||||
|
pthread_attr_setdetachstate(&udhcpc_thread_attr,
|
||||||
|
PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
if (profile->ipv4.Address) {
|
||||||
|
if (access("/usr/share/udhcpc/default.script", X_OK)) {
|
||||||
|
dbg_time(
|
||||||
|
"Fail to access /usr/share/udhcpc/default.script, "
|
||||||
|
"errno: %d (%s)",
|
||||||
|
errno, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-f,--foreground Run in foreground
|
||||||
|
//-b,--background Background if lease is not obtained
|
||||||
|
//-n,--now Exit if lease is not obtained
|
||||||
|
//-q,--quit Exit after obtaining lease
|
||||||
|
//-t,--retries N Send up to N discover packets (default 3)
|
||||||
|
snprintf(udhcpc_cmd, sizeof(udhcpc_cmd),
|
||||||
|
"busybox udhcpc -f -n -q -t 5 -i %s", ifname);
|
||||||
|
|
||||||
|
if (!access("/lib/netifd/dhcp.script", X_OK) &&
|
||||||
|
!access("/sbin/ifup", X_OK) &&
|
||||||
|
!access("/sbin/ifstatus", X_OK)) {
|
||||||
|
dbg_time("you are use OpenWrt?");
|
||||||
|
dbg_time("should not calling udhcpc manually?");
|
||||||
|
dbg_time("should modify /etc/config/network as below?");
|
||||||
|
dbg_time("config interface wan");
|
||||||
|
dbg_time("\toption ifname %s", ifname);
|
||||||
|
dbg_time("\toption proto dhcp");
|
||||||
|
dbg_time(
|
||||||
|
"should use \"/sbin/ifstaus wan\" to check %s 's status?",
|
||||||
|
ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_create(&udhcpc_thread_id, NULL, udhcpc_thread_function,
|
||||||
|
(void *)strdup(udhcpc_cmd));
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->ipv6.Address[0] && profile->ipv6.PrefixLengthIPAddr) {
|
||||||
|
// module do not support DHCPv6, only support 'Router Solicit'
|
||||||
|
// and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding,
|
||||||
|
// Kernel do not send RS
|
||||||
|
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
|
||||||
|
int forward_fd = open(forward_file, O_RDONLY);
|
||||||
|
if (forward_fd > 0) {
|
||||||
|
char forward_state[2];
|
||||||
|
read(forward_fd, forward_state, 2);
|
||||||
|
if (forward_state[0] == '1') {
|
||||||
|
dbg_time(
|
||||||
|
"%s enabled, kernel maybe donot send 'Router Solicit'",
|
||||||
|
forward_file);
|
||||||
|
}
|
||||||
|
close(forward_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_stop(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
char shell_cmd[128];
|
||||||
|
char reset_ip[128];
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
if (profile->qmapnet_adapter) {
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dibbler_client_alive) {
|
||||||
|
system("killall dibbler-client");
|
||||||
|
dibbler_client_alive = 0;
|
||||||
|
}
|
||||||
|
//snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down",
|
||||||
|
// profile->usbnet_adapter);
|
||||||
|
//fibo_system(shell_cmd);
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "busybox ifconfig %s down", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
snprintf(reset_ip, sizeof(reset_ip) - 1, "busybox ifconfig %s 0.0.0.0", ifname);
|
||||||
|
fibo_system(reset_ip);
|
||||||
|
}
|
421
package/wwan/app/fibocom-dial/src/udhcpc_netlink.c
Executable file
421
package/wwan/app/fibocom-dial/src/udhcpc_netlink.c
Executable file
@ -0,0 +1,421 @@
|
|||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <endian.h>
|
||||||
|
|
||||||
|
#include "libmnl/ifutils.h"
|
||||||
|
#include "libmnl/dhcp/dhcp.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "QMIThread.h"
|
||||||
|
|
||||||
|
static int fibo_system(const char *shell_cmd)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
dbg_time("%s", shell_cmd);
|
||||||
|
ret = system(shell_cmd);
|
||||||
|
if (ret) {
|
||||||
|
// dbg_time("Fail to system(\"%s\") = %d, errno: %d (%s)", shell_cmd,
|
||||||
|
// ret, errno, strerror(errno));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fibo_raw_ip_mode_check(const char *ifname)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
char raw_ip[128];
|
||||||
|
char mode[2] = "X";
|
||||||
|
int mode_change = 0;
|
||||||
|
|
||||||
|
snprintf(raw_ip, sizeof(raw_ip), "/sys/class/net/%s/qmi/raw_ip", ifname);
|
||||||
|
if (access(raw_ip, F_OK))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fd = open(raw_ip, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
dbg_time("%s %d fail to open(%s), errno:%d (%s)", __FILE__, __LINE__, raw_ip, errno, strerror(errno));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
read(fd, mode, 2);
|
||||||
|
if (mode[0] == '0' || mode[0] == 'N')
|
||||||
|
{
|
||||||
|
if_link_down(ifname);
|
||||||
|
dbg_time("echo Y > /sys/class/net/%s/qmi/raw_ip", ifname);
|
||||||
|
mode[0] = 'Y';
|
||||||
|
write(fd, mode, 2);
|
||||||
|
mode_change = 1;
|
||||||
|
if_link_up(ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
return mode_change;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fibo_set_driver_link_state(PROFILE_T *profile, int link_state)
|
||||||
|
{
|
||||||
|
char link_file[128];
|
||||||
|
int fd;
|
||||||
|
int new_state = 0;
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
snprintf(link_file, sizeof(link_file), "/sys/class/net/%s/link_state", profile->usbnet_adapter);
|
||||||
|
fd = open(link_file, O_RDWR | O_NONBLOCK | O_NOCTTY);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
if (errno != ENOENT)
|
||||||
|
dbg_time("Fail to access %s, errno: %d (%s)", link_file, errno, strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->qmap_mode <= 1)
|
||||||
|
new_state = !!link_state;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//0x80 means link off this pdp
|
||||||
|
new_state = (link_state ? 0x00 : 0x80) + profile->pdp;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(link_file, sizeof(link_file), "%d\n", new_state);
|
||||||
|
write(fd, link_file, sizeof(link_file));
|
||||||
|
|
||||||
|
if (link_state == 0 && profile->qmap_mode > 1)
|
||||||
|
{
|
||||||
|
size_t rc;
|
||||||
|
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
rc = read(fd, link_file, sizeof(link_file));
|
||||||
|
if (rc > 1 && (!strcasecmp(link_file, "0\n") || !strcasecmp(link_file, "0x0\n")))
|
||||||
|
{
|
||||||
|
// if_link_down(profile->usbnet_adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_start(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
char shell_cmd[512];
|
||||||
|
|
||||||
|
fibo_set_driver_link_state(profile, 1);
|
||||||
|
fibo_raw_ip_mode_check(ifname);
|
||||||
|
|
||||||
|
if (profile->qmapnet_adapter)
|
||||||
|
{
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu)
|
||||||
|
{
|
||||||
|
if_set_mtu(ifname, (profile->ipv4.Mtu));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(ifname, profile->usbnet_adapter))
|
||||||
|
{
|
||||||
|
//if_link_up(profile->usbnet_adapter);
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
//2021-02-01 willa.liu@fibocom.com changed begin for support mantis 0069837
|
||||||
|
if_link_up(ifname);
|
||||||
|
|
||||||
|
#if 1 //for bridge mode, only one public IP, so do udhcpc manually
|
||||||
|
if (fibo_bridge_mode_detect(profile))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// if use DHCP(should make with ${DHCP} src files)
|
||||||
|
// do_dhcp(ifname);
|
||||||
|
// return 0;
|
||||||
|
/* IPv4 Addr Info */
|
||||||
|
if (profile->ipv4.Address)
|
||||||
|
{
|
||||||
|
dbg_time("IPv4 MTU: %d", profile->ipv4.Mtu);
|
||||||
|
dbg_time("IPv4 Address: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Address)));
|
||||||
|
dbg_time("IPv4 Netmask: %d", mask_to_prefix_v4(ntohl(profile->ipv4.SubnetMask)));
|
||||||
|
dbg_time("IPv4 Gateway: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.Gateway)));
|
||||||
|
dbg_time("IPv4 DNS1: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsPrimary)));
|
||||||
|
dbg_time("IPv4 DNS2: %s", ipaddr_to_string_v4(ntohl(profile->ipv4.DnsSecondary)));
|
||||||
|
if_set_network_v4(ifname, ntohl(profile->ipv4.Address),
|
||||||
|
mask_to_prefix_v4(profile->ipv4.SubnetMask),
|
||||||
|
ntohl(profile->ipv4.Gateway),
|
||||||
|
ntohl(profile->ipv4.DnsPrimary),
|
||||||
|
ntohl(profile->ipv4.DnsSecondary));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbg_time("The IPv4 Address in profile is NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->ipv6.Address && (profile->ipv6.PrefixLengthIPAddr != 0))
|
||||||
|
{
|
||||||
|
//module do not support DHCPv6, only support 'Router Solicit'
|
||||||
|
//and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS
|
||||||
|
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
|
||||||
|
int forward_fd = open(forward_file, O_RDONLY);
|
||||||
|
if (forward_fd > 0)
|
||||||
|
{
|
||||||
|
char forward_state[2];
|
||||||
|
read(forward_fd, forward_state, 2);
|
||||||
|
if (forward_state[0] == '1')
|
||||||
|
{
|
||||||
|
dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file);
|
||||||
|
}
|
||||||
|
close(forward_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg_time("IPv6 MTU: %d", profile->ipv6.Mtu);
|
||||||
|
dbg_time("IPv6 Address: %s", ipaddr_to_string_v6(profile->ipv6.Address));
|
||||||
|
dbg_time("IPv6 PrefixLengthIPAddr: %d", profile->ipv6.PrefixLengthIPAddr);
|
||||||
|
dbg_time("IPv6 Gateway: %s", ipaddr_to_string_v6(profile->ipv6.Gateway));
|
||||||
|
dbg_time("IPv6 DNS1: %s", ipaddr_to_string_v6(profile->ipv6.DnsPrimary));
|
||||||
|
dbg_time("IPv6 DNS2: %s", ipaddr_to_string_v6(profile->ipv6.DnsSecondary));
|
||||||
|
if_set_network_v6(ifname, profile->ipv6.Address, profile->ipv6.PrefixLengthIPAddr,
|
||||||
|
profile->ipv6.Gateway, profile->ipv6.DnsPrimary, profile->ipv6.DnsSecondary);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dbg_time("The IPv6 Address in profile is NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_stop(PROFILE_T *profile)
|
||||||
|
{
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
fibo_set_driver_link_state(profile, 0);
|
||||||
|
|
||||||
|
if (profile->qmapnet_adapter)
|
||||||
|
{
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if_flush_v4_addr(ifname);
|
||||||
|
if_flush_v6_addr(ifname);
|
||||||
|
if_link_down(ifname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fibo_set_mtu(const char *ifname, int ifru_mtu) {
|
||||||
|
int inet_sock;
|
||||||
|
struct ifreq ifr;
|
||||||
|
|
||||||
|
inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
|
||||||
|
if (inet_sock > 0) {
|
||||||
|
strcpy(ifr.ifr_name, ifname);
|
||||||
|
|
||||||
|
if (!ioctl(inet_sock, SIOCGIFMTU, &ifr)) {
|
||||||
|
if (ifr.ifr_ifru.ifru_mtu != ifru_mtu) {
|
||||||
|
dbg_time("change mtu %d -> %d", ifr.ifr_ifru.ifru_mtu , ifru_mtu);
|
||||||
|
ifr.ifr_ifru.ifru_mtu = ifru_mtu;
|
||||||
|
ioctl(inet_sock, SIOCSIFMTU, &ifr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(inet_sock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* udhcpc_thread_function(void* arg) {
|
||||||
|
FILE * udhcpc_fp;
|
||||||
|
char *udhcpc_cmd = (char *)arg;
|
||||||
|
|
||||||
|
if (udhcpc_cmd == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dbg_time("%s", udhcpc_cmd);
|
||||||
|
udhcpc_fp = popen(udhcpc_cmd, "r");
|
||||||
|
free(udhcpc_cmd);
|
||||||
|
if (udhcpc_fp) {
|
||||||
|
char buf[0xff];
|
||||||
|
|
||||||
|
buf[sizeof(buf)-1] = '\0';
|
||||||
|
while((fgets(buf, sizeof(buf)-1, udhcpc_fp)) != NULL) {
|
||||||
|
if ((strlen(buf) > 1) && (buf[strlen(buf) - 1] == '\n'))
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
dbg_time("%s", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
pclose(udhcpc_fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_start_pcie(PROFILE_T *profile) {
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
char sub_intf_name[100] = {0};
|
||||||
|
int intf_id = 0;
|
||||||
|
char shell_cmd[512];
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
if (profile->qmapnet_adapter) {
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->muxid > 0x81)
|
||||||
|
{
|
||||||
|
intf_id = profile->muxid - 0x81;
|
||||||
|
snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->rawIP && profile->ipv4.Address && profile->ipv4.Mtu) {
|
||||||
|
fibo_set_mtu(ifname, (profile->ipv4.Mtu));
|
||||||
|
}
|
||||||
|
|
||||||
|
fibo_system("echo 1 > /sys/module/fibo_mhi/parameters/macaddr_check");
|
||||||
|
|
||||||
|
if (strcmp(ifname, profile->usbnet_adapter)) {
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", profile->usbnet_adapter);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For IPv6, down & up will send protocol packets, and that's needed.
|
||||||
|
if (profile->ipv6_flag && profile->muxid <= 0x81) {
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s up", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
//for bridge mode, only one public IP, so do udhcpc manually
|
||||||
|
if (fibo_bridge_mode_detect(profile)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do DHCP using busybox tools */
|
||||||
|
{
|
||||||
|
char udhcpc_cmd[128];
|
||||||
|
pthread_attr_t udhcpc_thread_attr;
|
||||||
|
pthread_t udhcpc_thread_id;
|
||||||
|
|
||||||
|
pthread_attr_init(&udhcpc_thread_attr);
|
||||||
|
pthread_attr_setdetachstate(&udhcpc_thread_attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
if (profile->ipv4.Address)
|
||||||
|
{
|
||||||
|
char v4add_str[32] = {0};
|
||||||
|
char v4gw_str[32] = {0};
|
||||||
|
char v4_netmask_str[32] = {0};
|
||||||
|
uint32_t Address = ntohl(profile->ipv4.Address);
|
||||||
|
uint32_t Gateway = ntohl(profile->ipv4.Gateway);
|
||||||
|
uint32_t SubnetMask = ntohl(profile->ipv4.SubnetMask);
|
||||||
|
inet_ntop(AF_INET, &Address, v4add_str, sizeof(v4add_str));
|
||||||
|
inet_ntop(AF_INET, &Gateway, v4gw_str, sizeof(v4gw_str));
|
||||||
|
inet_ntop(AF_INET, &SubnetMask, v4_netmask_str, sizeof(v4_netmask_str));
|
||||||
|
|
||||||
|
if (profile->muxid == 0x81)
|
||||||
|
{
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", ifname, v4add_str, v4_netmask_str);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route add default gw %s dev %s", v4gw_str, ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
else if (profile->muxid > 0x81)
|
||||||
|
{
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s %s netmask %s", sub_intf_name, v4add_str, v4_netmask_str);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->ipv6.PrefixLengthIPAddr)
|
||||||
|
{
|
||||||
|
//module do not support DHCPv6, only support 'Router Solicit'
|
||||||
|
//and it seem if enable /proc/sys/net/ipv6/conf/all/forwarding, Kernel do not send RS
|
||||||
|
const char *forward_file = "/proc/sys/net/ipv6/conf/all/forwarding";
|
||||||
|
int forward_fd = open(forward_file, O_RDONLY);
|
||||||
|
if (forward_fd > 0) {
|
||||||
|
char forward_state[2];
|
||||||
|
read(forward_fd, forward_state, 2);
|
||||||
|
if (forward_state[0] == '1') {
|
||||||
|
dbg_time("%s enabled, kernel maybe donot send 'Router Solicit'", forward_file);
|
||||||
|
}
|
||||||
|
close(forward_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char v6add_str[100] = {0};
|
||||||
|
char v6gw_str[100] = {0};
|
||||||
|
inet_ntop(AF_INET6, profile->ipv6.Address, v6add_str, sizeof(v6add_str));
|
||||||
|
inet_ntop(AF_INET6, profile->ipv6.Gateway, v6gw_str, sizeof(v6gw_str));
|
||||||
|
|
||||||
|
if (profile->muxid == 0x81)
|
||||||
|
{
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", ifname, v6add_str, profile->ipv6.PrefixLengthIPAddr);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
else if (profile->muxid > 0x81)
|
||||||
|
{
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link add link %s name %s type vlan id %d", ifname, sub_intf_name, intf_id);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link set %s up", sub_intf_name);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s add %s/%d", sub_intf_name, v6add_str, profile->ipv6.PrefixLengthIPAddr);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
/* start 2021-01-21 add by haopengfei to fix mantis 69056 */
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "route -A inet6 add ::/0 gw %s dev %s", v6gw_str, sub_intf_name);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
/* end 2021-01-21 add by haopengfei to fix mantis 69056 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void udhcpc_stop_pcie(PROFILE_T *profile) {
|
||||||
|
char *ifname = profile->usbnet_adapter;
|
||||||
|
char shell_cmd[128];
|
||||||
|
char reset_ip[128];
|
||||||
|
dbg_time("enter %s ", __func__);
|
||||||
|
|
||||||
|
if (profile->qmapnet_adapter) {
|
||||||
|
ifname = profile->qmapnet_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->muxid == 0x81)
|
||||||
|
{
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ifconfig %s down", ifname);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
snprintf(reset_ip, sizeof(reset_ip) - 1, "ifconfig %s 0.0.0.0", ifname);
|
||||||
|
fibo_system(reset_ip);
|
||||||
|
}
|
||||||
|
else if (profile->muxid > 0x81)
|
||||||
|
{
|
||||||
|
char sub_intf_name[100];
|
||||||
|
int intf_id = profile->muxid - 0x81;
|
||||||
|
snprintf(sub_intf_name, sizeof(sub_intf_name) - 1, "%s.%d", ifname, intf_id);
|
||||||
|
|
||||||
|
snprintf(shell_cmd, sizeof(shell_cmd) - 1, "ip link del link dev %s", sub_intf_name);
|
||||||
|
fibo_system(shell_cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
142
package/wwan/app/fibocom-dial/src/util.c
Executable file
142
package/wwan/app/fibocom-dial/src/util.c
Executable file
@ -0,0 +1,142 @@
|
|||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "QMIThread.h"
|
||||||
|
|
||||||
|
#if defined(__STDC__)
|
||||||
|
#include <stdarg.h>
|
||||||
|
#define __V(x) x
|
||||||
|
#else
|
||||||
|
#include <varargs.h>
|
||||||
|
#define __V(x) (va_alist) va_dcl
|
||||||
|
#define const
|
||||||
|
#define volatile
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#define is_bigendian() ((*(char *)&i) == 0)
|
||||||
|
FILE *logfilefp = NULL;
|
||||||
|
static pthread_mutex_t printfMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static char line[1024];
|
||||||
|
const int i = 1;
|
||||||
|
|
||||||
|
// defined in atchannel.c
|
||||||
|
static void setTimespecRelative(struct timespec *p_ts, long long msec)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv, (struct timezone *)NULL);
|
||||||
|
|
||||||
|
/* what's really funny about this is that I know
|
||||||
|
pthread_cond_timedwait just turns around and makes this
|
||||||
|
a relative time again */
|
||||||
|
p_ts->tv_sec = tv.tv_sec + (msec / 1000);
|
||||||
|
p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L) * 1000L;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pthread_cond_timeout_np(pthread_cond_t *cond, pthread_mutex_t *mutex,
|
||||||
|
unsigned msecs)
|
||||||
|
{
|
||||||
|
if (msecs != 0) {
|
||||||
|
struct timespec ts;
|
||||||
|
setTimespecRelative(&ts, msecs);
|
||||||
|
return pthread_cond_timedwait(cond, mutex, &ts);
|
||||||
|
} else {
|
||||||
|
return pthread_cond_wait(cond, mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_time(void)
|
||||||
|
{
|
||||||
|
static char time_buf[50];
|
||||||
|
struct timeval tv;
|
||||||
|
time_t time;
|
||||||
|
suseconds_t millitm;
|
||||||
|
struct tm *ti;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
|
||||||
|
time = tv.tv_sec;
|
||||||
|
millitm = (tv.tv_usec + 500) / 1000;
|
||||||
|
|
||||||
|
if (millitm == 1000) {
|
||||||
|
++time;
|
||||||
|
millitm = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ti = localtime(&time);
|
||||||
|
sprintf(time_buf, "[%02d-%02d_%02d:%02d:%02d:%03d]", ti->tm_mon + 1,
|
||||||
|
ti->tm_mday, ti->tm_hour, ti->tm_min, ti->tm_sec, (int)millitm);
|
||||||
|
return time_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbg_time(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&printfMutex);
|
||||||
|
|
||||||
|
snprintf(line, sizeof(line), "%s ", get_time());
|
||||||
|
|
||||||
|
vsnprintf(line + strlen(line), sizeof(line) - strlen(line), fmt, args);
|
||||||
|
fprintf(stdout, "%s\n", line);
|
||||||
|
|
||||||
|
if (logfilefp) {
|
||||||
|
fprintf(logfilefp, "%s\n", line);
|
||||||
|
}
|
||||||
|
fflush(logfilefp);
|
||||||
|
pthread_mutex_unlock(&printfMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
USHORT le16_to_cpu(USHORT v16)
|
||||||
|
{
|
||||||
|
USHORT tmp = v16;
|
||||||
|
if (is_bigendian()) {
|
||||||
|
unsigned char *s = (unsigned char *)(&v16);
|
||||||
|
unsigned char *d = (unsigned char *)(&tmp);
|
||||||
|
d[0] = s[1];
|
||||||
|
d[1] = s[0];
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT le32_to_cpu(UINT v32)
|
||||||
|
{
|
||||||
|
UINT tmp = v32;
|
||||||
|
if (is_bigendian()) {
|
||||||
|
unsigned char *s = (unsigned char *)(&v32);
|
||||||
|
unsigned char *d = (unsigned char *)(&tmp);
|
||||||
|
d[0] = s[3];
|
||||||
|
d[1] = s[2];
|
||||||
|
d[2] = s[1];
|
||||||
|
d[3] = s[0];
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
USHORT cpu_to_le16(USHORT v16)
|
||||||
|
{
|
||||||
|
USHORT tmp = v16;
|
||||||
|
if (is_bigendian()) {
|
||||||
|
unsigned char *s = (unsigned char *)(&v16);
|
||||||
|
unsigned char *d = (unsigned char *)(&tmp);
|
||||||
|
d[0] = s[1];
|
||||||
|
d[1] = s[0];
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
UINT cpu_to_le32(UINT v32)
|
||||||
|
{
|
||||||
|
UINT tmp = v32;
|
||||||
|
if (is_bigendian()) {
|
||||||
|
unsigned char *s = (unsigned char *)(&v32);
|
||||||
|
unsigned char *d = (unsigned char *)(&tmp);
|
||||||
|
d[0] = s[3];
|
||||||
|
d[1] = s[2];
|
||||||
|
d[2] = s[1];
|
||||||
|
d[3] = s[0];
|
||||||
|
}
|
||||||
|
return tmp;
|
||||||
|
}
|
53
package/wwan/app/fibocom-dial/src/util.h
Executable file
53
package/wwan/app/fibocom-dial/src/util.h
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2010 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UTILS_H_
|
||||||
|
#define _UTILS_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct listnode {
|
||||||
|
struct listnode *next;
|
||||||
|
struct listnode *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define node_to_item(node, container, member) \
|
||||||
|
(container *)(((char *)(node)) - offsetof(container, member))
|
||||||
|
|
||||||
|
#define list_declare(name) \
|
||||||
|
struct listnode name = { \
|
||||||
|
.next = &name, \
|
||||||
|
.prev = &name, \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define list_for_each(node, list) \
|
||||||
|
for (node = (list)->next; node != (list); node = node->next)
|
||||||
|
|
||||||
|
#define list_for_each_reverse(node, list) \
|
||||||
|
for (node = (list)->prev; node != (list); node = node->prev)
|
||||||
|
void dbg_time(const char *fmt, ...);
|
||||||
|
void list_init(struct listnode *list);
|
||||||
|
void list_add_tail(struct listnode *list, struct listnode *item);
|
||||||
|
void list_add_head(struct listnode *head, struct listnode *item);
|
||||||
|
void list_remove(struct listnode *item);
|
||||||
|
|
||||||
|
#define list_empty(list) ((list) == (list)->next)
|
||||||
|
#define list_head(list) ((list)->next)
|
||||||
|
#define list_tail(list) ((list)->prev)
|
||||||
|
|
||||||
|
int epoll_register(int epoll_fd, int fd, unsigned int events);
|
||||||
|
int epoll_deregister(int epoll_fd, int fd);
|
||||||
|
#endif
|
@ -24,9 +24,9 @@ endef
|
|||||||
|
|
||||||
define Package/luci-proto-3x
|
define Package/luci-proto-3x
|
||||||
$(call Package/luci-proto-3x/Default)
|
$(call Package/luci-proto-3x/Default)
|
||||||
SECTION:=net
|
SECTION:=luci
|
||||||
CATEGORY:=ROOter
|
CATEGORY:=LuCI
|
||||||
SUBMENU:=Protocols
|
SUBMENU:=5. Protocols
|
||||||
TITLE:=Support for 3x
|
TITLE:=Support for 3x
|
||||||
endef
|
endef
|
||||||
|
|
||||||
|
63
package/wwan/app/quectel_cm_5G/src/default.script
Normal file
63
package/wwan/app/quectel_cm_5G/src/default.script
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Busybox udhcpc dispatcher script. Copyright (C) 2009 by Axel Beckert.
|
||||||
|
#
|
||||||
|
# Based on the busybox example scripts and the old udhcp source
|
||||||
|
# package default.* scripts.
|
||||||
|
|
||||||
|
RESOLV_CONF="/etc/resolv.conf"
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
bound|renew)
|
||||||
|
[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
|
||||||
|
[ -n "$subnet" ] && NETMASK="netmask $subnet"
|
||||||
|
|
||||||
|
/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
|
||||||
|
|
||||||
|
if [ -n "$router" ]; then
|
||||||
|
echo "$0: Resetting default routes"
|
||||||
|
while /sbin/route del default gw 0.0.0.0 dev $interface; do :; done
|
||||||
|
|
||||||
|
metric=0
|
||||||
|
for i in $router; do
|
||||||
|
/sbin/route add default gw $i dev $interface metric $metric
|
||||||
|
metric=$(($metric + 1))
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update resolver configuration file
|
||||||
|
R=""
|
||||||
|
[ -n "$domain" ] && R="domain $domain
|
||||||
|
"
|
||||||
|
for i in $dns; do
|
||||||
|
echo "$0: Adding DNS $i"
|
||||||
|
R="${R}nameserver $i
|
||||||
|
"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -x /sbin/resolvconf ]; then
|
||||||
|
echo -n "$R" | resolvconf -a "${interface}.udhcpc"
|
||||||
|
else
|
||||||
|
echo -n "$R" > "$RESOLV_CONF"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
deconfig)
|
||||||
|
if [ -x /sbin/resolvconf ]; then
|
||||||
|
resolvconf -d "${interface}.udhcpc"
|
||||||
|
fi
|
||||||
|
/sbin/ifconfig $interface 0.0.0.0
|
||||||
|
;;
|
||||||
|
|
||||||
|
leasefail)
|
||||||
|
echo "$0: Lease failed: $message"
|
||||||
|
;;
|
||||||
|
|
||||||
|
nak)
|
||||||
|
echo "$0: Received a NAK: $message"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "$0: Unknown udhcpc command: $1";
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
esac
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user