WWAN: add Fibocom linux usb QMI WWAN driver and dial app

This commit is contained in:
coolsnowwolf 2023-02-28 00:09:36 +08:00
parent 15d1af2d95
commit 012f1419de
113 changed files with 20530 additions and 5 deletions

View 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))

View 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,
};

View 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

View 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

View 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);
}

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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,
};

File diff suppressed because it is too large Load Diff

View 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>

View 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;
}
/**
* @}
*/

View 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);
}
/**
* @}
*/

View File

@ -0,0 +1,5 @@
#ifndef __DHCP_H__
#define __DHCP_H__
int do_dhcp(char *iname);
#endif //__DHCP_H__

View 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);
}

View 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);
}

View 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

View 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;
}

View 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

View 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;
}

View 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__

View 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

View 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;
}
/**
* @}
*/

View 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);
}
/**
* @}
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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=

View 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;
}

View 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;
}

View 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

View 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);
}

View 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);
}
}

View 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;
}

View 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

View File

@ -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

View 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