分类目录归档:未分类

IPV6

无状态地址自动配置(SLAAC)

当连接到IPv6网络上时,IPv6主机可以使用邻居发现协议对自身进行自动配置。当第一次连接到网络上时,主机发送一个链路本地路由器请求(solicitation)多播请求来获取配置参数。路由器使用包含Internet层配置参数的路由器宣告(advertisement)报文进行回应。

在不适合使用IPv6无状态地址自动配置的场景下,网络可以使用有状态配置(DHCPv6),或者使用静态方法手动配置。

IPv6地址可分为三种:

单播(unicast)地址
单播地址标示一个网络接口。协议会把送往地址的数据包送往给其接口。IPv6的单播地址可以有一个代表特殊地址名字的范畴,如链路本地地址(link local address)和唯一区域地址(ULA,unique local address)。单播地址包括可聚类的全球单播地址、链路本地地址等。

任播(anycast)地址
任播像是Unicast(单点传播)与Broadcast(多点广播)的综合。单点广播在来源和目的地间直接进行通信;多点广播存在于单一来源和多个目的地进行通信。
而Anycast则在以上两者之间,它像多点广播(Broadcast)一样,会有一组接收节点的地址列表,但指定为Anycast的数据包,只会发送给距离最近或发送成本最低(根据路由表来判断)的其中一个接收地址,当该接收地址收到数据包并进行回应,且加入后续的传输。该接收列表的其他节点,会知道某个节点地址已经回应了,它们就不再加入后续的传输作业。
以目前的应用为例,Anycast地址只能分配给中间设备(如路由器、三层交换机等),不能分配给终端设备(手机、电脑等),而且不能作为发送端的地址。

多播(multicast)地址
多播地址也称组播地址。多播地址也被指定到一群不同的接口,送到多播地址的数据包会被发送到所有的地址。多播地址由皆为一的字节起始,亦即:它们的前置为FF00::/8。其第二个字节的最后四个比特用以标明”范畴”。
一般有node-local(0x1)、link-local(0x2)、site-local(0x5)、organization-local(0x8)和global(0xE)。多播地址中的最低112位会组成多播组群标识符,不过因为传统方法是从MAC地址产生,故只有组群标识符中的最低32位有使用。定义过的组群标识符有用于所有节点的多播地址0x1和用于所有路由器的0x2。
另一个多播组群的地址为”solicited-node多播地址”,是由前置FF02::1:FF00:0/104和剩余的组群标识符(最低24位)所组成。这些地址允许经由邻居发现协议(NDP,Neighbor Discovery Protocol)来解译链接层地址,因而不用干扰到在区网内的所有节点。

IPv6中有些地址是有特殊含义的:

未指定地址

::/128-所有比特皆为零的地址称作未指定地址。这个地址不可指定给某个网络接口,并且只有在主机尚未知道其来源IP时,才会用于软件中。路由器不可转送包含未指定地址的数据包。

链路本地地址

::1/128-是一种单播绕回地址。如果一个应用程序将数据包送到此地址,IPv6堆栈会转送这些数据包绕回到同样的虚拟接口(相当于IPv4中的127.0.0.1/8)。
fe80::/10-这些链路本地地址指明,这些地址只在区域连线中是合法的,这有点类似于IPv4中的169.254.0.0/16。

链路本地地址前缀具有以下格式:fe80::interface-ID/10
当一个节点启用 IPv6 时自动生成,interface-ID 通常从 48 位 MAC 地址派生而来。

用于单一链路,适用于自动配置、NDP(Neighbor Discovery Protocol,邻居发现协议)等实现,路由器不转发以 fe80 开头的地址到 Link。前 64 位为 fe80::,而后 64 位为 Interface ID。可见,同一个 Link 上的所有 Interfaces 的前 64 位都是相同的,再通过后 64 的 Interface ID 来唯一标识一个 Interface。所以,Link-Local Address 只在 Subnet 中有效,即使 Link 上并没有路由器,Interfaces 之间也是可以通信的。

唯一区域地址

fc00::/7-唯一区域地址(ULA,unique local address)只可用于本地通信,类似于IPv4的专用网络地址10.0.0.0/8、172.16.0.0/12和192.168.0.0/16。这定义在RFC 4193中,是用来取代站点本地位域。这地址包含一个40比特的伪随机数,以减少当网站合并或数据包误传到网络时碰撞的风险。这些地址除了只能用于区域外,还具备全局性的范畴,这点违反了唯一区域位域所取代的站点本地地址的定义。

多播地址

ff00::/8-这个前置表明定义在”IP Version 6 Addressing Architecture”(RFC 4291)中的多播地址, 通过第二段的最后四位来标识多播的 “范畴”。其中有些地址已用于指定特殊协议,例如 ff0X::101 对应所有区域的 NTP 服务器(RFC 2375)。

标志位为 0000 表示是永久保留的组播地址,分配给各种技术使用;标志位为0001 表示是用户可使用的临时组播地址。范围段定义了组播地址的范围,其定义如下:

0001:本地接口范围。
0010:本地链路范围。
0011:本地子网范围。
0100:本地管理范围。
0101:本地站点范围,类似组播的私网地址。
1000:组织机构范围。
1110:全球范围,类似组播的公网地址。

下面是一些组播指定地址:

FF02::1,在本地链路范围的所有节点。
FF02::2,在本地链路范围的所有路由器。
FF02::5,all ospf routers。
FF02::9,所有运行 RIP 的路由器。
FF02::A,所有运行 eigrp 的路由器。
FF05::2,在一个站点范围内的所有路由器。

在创建某个接口的 IPv6 单点传送地址时,内核会自动使该接口成为某些多点传送组的成员。例如,内核会使每个节点都成为 “请求节点” 多点传送组的成员,相邻节点搜索协议使用该组来检测可访问性。内核还自动使节点成为 “所有节点” 或 “所有路由器” 多点传送组的成员。

请求节点多播地址(Solicited-node multicast address)

ff02::1:FFXX:XXXX-XX:XXXX为相对应的单播或任播地址中的三个最低的字节。

IPv4转译地址

::ffff:x.x.x.x/96-用于IPv4映射地址。(参见以下的转换机制)。
2001::/32-用于Teredo隧道。
2002::/16-用于6to4。

ORCHID

2001:10::/28-ORCHID (Overlay Routable Cryptographic Hash Identifiers)(RFC 4843)。这些是不可绕送的IPv6地址,用于加密散列识别。

文件

2001:db8::/32-这前置用于文件(RFC 3849)。这些地址应用于IPV6地址的示例中,或描述网络架构。


邻居发现协议
Neighbor Discovery Protocol简称:NDP或ND, 工作在数据链路层(也有观点认为是第3层协议),负责在链路上发现其他节点和相应的IP地址,并确定可用路由和维护关于可用路径和其他活动节点的信息可达性

邻居发现协议定义了五种ICMPv6类型

路由请求 (Router Solicitation)
路由通告 (Router Advertisement)
邻居请求 (Neighbor Solicitation)
邻居通告 (Neighbor Advertisement)
重定向 (Redirect)


常用名词:

Node(节点):具有 IPv6 地址且接口配置为支持 IPv6 的任何系统。该专业术语适用于主机和路由器。

相邻节点:与本地节点在同一个链路上的 IPv6 节点。

Router(IPv6 路由器):用来转发 IPv6 包的节点。路由器必须至少有一个接口配置为支持 IPv6。IPv6 路由器还可以通过内部网络通告企业的已注册 IPv6 站点前缀。

Host(IPv6 主机):具有 IPv6 地址的节点。IPv6 主机可以有多个配置为支持 IPv6 的接口。与 IPv4 主机一样,IPv6 主机也不转发包。

Link(链路):单一且连续的网络介质,其两端均连接有路由器。包括:以太网、PPP 链路、X.25、帧中继、ATM 等。

链路 MTU:链路能传输的最大单位,即最大的 IPv6 报文字节数。

Subnet(IPv6 子网):IPv6 网络的管理段。与 IPv4 子网的组件一样,IPv6 子网的组件也可以直接对应于链路上的所有节点。必要时,可以在单独的子网中对链路上的节点进行管理。另外,IPv6 还支持多链路子网,在多链路子网上,多个链路上的节点可以是同一个子网的组件。

IPv6 隧道:在一个 IPv6 节点和另一个 IPv6 节点端点之间提供虚拟的点对点路径的隧道。IPv6 支持可手动配置的隧道和 6to4 自动隧道。
边界路由器:位于网络边界的路由器,是通往本地网络外部端点的 IPv6 隧道的一个端点。此路由器必须至少有一个连接到内部网络的 IPv6 接口。对于外部网络,此路由器可以有一个 IPv6 接口或一个 IPv4 接口。

Site(站点):多个 Subnet 构成一个 Site。

路径 MTU:IPv6 源端和目的端之间能传输的最大的 IPv6 报文字节数,通常是路径中所有链路的最小链路 MTU


路由器搜索: 帮助主机查找本地链路上的路由器。
地址自动配置: 使节点能够为其接口自动配置 IPv6 地址。
前缀搜索: 使节点能够搜索已分配给链路的已知子网前缀。节点使用前缀来区分位于本地链路上的目标和那些只能通过路由器来访问的目标。(类似于 IPV4 本地子网概念)
地址解析: 帮助节点确定相邻节点的链路本地地址(如果只给定目标的 IP 地址)。
确定下一个跃点: 使用某种算法来确定本地链路之外的包接受者的跃点的 IP 地址。下一个跃点可以是路由器或目标节点。
相邻节点无法访问检测: 帮助节点确定相邻节点是否不再可以访问。对于路由器和主机,可以重复进行地址解析。
重复地址检测: 使节点能够确定其要使用的地址是否尚未被使用。
重定向: 使路由器能够通知主机要用于到达特定目标的较好的第一个跃点节点。


IPv6 主机能够使用唯一的链路本地地址来自动配置自身的 IPv6 地址,一旦主机获得了 IPv6 地址,它就加入了多个组播组。与该段相关的所有通信仅在那些组播地址上发生。

邻居请求:通过 DHCP 自动获取或手动配置 IPv6 地址之后,主机向 FF02 :: 1/16 多播地址发送邻居请求消息,以此来确定没有 IPv6 地址冲突。
DAD(重复地址检测):当主机不侦听来自段中关于其邻居请求消息的任何内容时,假定段上没有重复地址。
邻居通告:在将地址分配给其接口并使其启动和运行后,主机再次发出邻居通告消息,通知该段上的所有其他主机,它已分配这些 IPv6 地址其接口,其他的主机就不要再继续重复分配了。


一旦主机完成了其 IPv6 地址的配置,它会执行以下操作:

路由器请求:主机 发送路由器请求 FF02 :: 2/16 组播数据包,以了解此链路上所有存在的路由器。它帮助主机将路由器配置为其默认网关。如果其默认网关路由器关闭,主机可以切换到新的路由器,并使其成为默认网关。
路由器通告:当路由器接收到路由器请求消息时,它回应主机,通告它在该链路上的存在。
重定向:路由器收到了主机发出的路由器请求,但它知道自己并不是该主机的最佳网关。在这种情况下,路由器会响应一个重定向消息,告诉主机有一个更好的 “下一跳” 路由器可用。


ICMPv6 报文(作为 IPv6 扩展头部)封装在 L3 报文中,可视为 3 层协议。NDP 在 L3 实现,优点是:可利用 L3 安全特性;可对不同介质的 L2 使用同一种地址解析协议;可使用组播替代广播,减轻 L2 网络压力。

android网络共享

网络共享模块可将 Android 设备的互联网连接与连接的其他客户端设备共享,这些设备可以通过 Wi-Fi、USB、蓝牙或以太网连接到网络共享设备。此模块包含网络共享组件(USB、Wi-Fi 接入点、蓝牙等)及其依赖项(与网络共享权限、IpServer 和 offloadController 的交互)。

1. 对于 IPv4 网络,该模块会设置网络地址转换 (NAT),并采用动态主机配置协议 (DHCP) 进行 IP 地址分配。
2. 对于 IPv6 网络,该模块会采用 IPv6 无状态地址自动配置 (SLAAC) 进行 IP 地址分配。

usb 4g dongle在openwrt上速率很慢的可能解决方法

要求Linux内核版本在 4.14以上
kmod-ipt-offload
kmod-nft-offload
kmod-nf-flow
kmod-ipt-raw

然后在 /etc/config/firewall 的default section里设置

option flow_offloading 1

配置上面几个驱动后,会自动 设置好这个选项

安装 android usb网络共享驱动
kmod-usb-net-rndis

查看usb设备

cat /sys/kernel/debug/usb/devices

开启usb网口

uci set network.wan.ifname="usb0"
uci set network.wan6.ifname="usb0"
uci commit network
/etc/init.d/network restart

星际宝盒CM520-79F刷机

https://www.right.com.cn/forum/thread-2451649-1-1.html
https://www.right.com.cn/forum/thread-3077311-1-1.html

最难的是拆掉底部的4个塑料片,它们挡住了螺丝。
用刀片或者最小最薄的一字螺丝刀,奋力“挑”开

 picocom  -b 115200  /dev/ttyUSB0
picocom v3.1

port is        : /dev/ttyUSB0
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        : 
omap is        : 
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready
In:    serial
Out:   serial
Err:   serial
machid: 8010001
flash_type: 2
Hit any key to stop autoboot:  0 

按任意键盘进入uboot命令模式, 可以看到 (IPQ40XX): 提示符号

使用print命令查看当前uboot环境变量,查看serverip,然后将电脑ip固定为所查询到的ip
也可以用命令设置

setenv serverip 192.168.1.188

在Linux下可以用
https://github.com/DarinM223/tftp-server
https://github.com/sirMackk/py3tftp
做FTP
python3 main.py -p 69
要以root运行在默认端口69才可以

python daemon systemd servicce

现在的Linux基本用systemd 来管理系统服务(system’s services or daemons)

用户级别的服务
~/.config/systemd/user/python_demo_service.service

查看服务

systemctl --user list-unit-files | grep python_demo_service

参考:
https://github.com/torfsen/python-systemd-tutorial
https://medium.com/codex/setup-a-python-script-as-a-service-through-systemctl-systemd-f0cc55a42267

依赖:
https://github.com/systemd/python-systemd
https://peps.python.org/pep-3143/
https://pypi.org/project/python-daemon/
https://pypi.org/project/setproctitle/

结论:
用systemd ,然后在 main 进程里跑,不需要 daemonalize

Android BLE 连接错误133

在bluedroid gatt_api.h文件中有定义
#define GATT_ERROR 0x0085
https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#constants_2

情形一:
多次重复连接,断开,连接,断开,再连接,就可能出现此错误
调用 disconnect()后,并没有调用 close()去释放资源。
有些手机一次最多只能处理6个连接
如果依次连接 A B C D E F G 7个设备, 不用close()释放资源,到第7个设备
也会报 133 通用错误

情形2:
connectGatt 时 没有使用 BluetoothDevice.TRANSPORT_LE 参数

解决方法3:

connectGatt调用时, autoconnect设置为false会加快连接速度, 但这也会导致, 30秒内还连接不上,就会到133

设备电量低,信号弱
两个设备距离远,信号也弱
这些都会导致133错误

重新connect
或者 重新scan, 再connect

Null out any references to this BluetoothGatt object.
清空所有对 BluetoothGatt的引用。

解决方法4:
关掉蓝牙,再开
关掉手机,再开

保持蓝牙BLE非绑定设备的后台连接

对于非绑定设备(也就是扫描后连接的这种模式),Android 上 BLE 的默认行为是应用拥有与 BLE 设备的连接。相比之下,Android 操作系统拥有绑定设备的连接。

这意味着对于非绑定设备的情形,如果您的应用程序由于资源限制在后台被操作系统终止,或者如果您的用户将您的应用程序滑开(立即终止它,效果明显😃),BLE 连接将丢失,您应该会看到大多数 BLE 外围设备再次开始广告。

尝试使您的应用程序进程尽可能长时间保持活动状态的最直接方法是使用前台服务。如何实现自己的前台服务,在 官方文档里 有明确的步骤。
foreground-services

前台服务本质上是一个带着 通知抽屉中的持久横幅 运行的服务(也就是状态栏有个通知 ),让用户了解您的应用程序正在后台执行某些操作。(A foreground service is essentially a Service that runs with a persistent banner in the notification drawer, keeping the user in the loop that your app is doing something in the background. )
由于内存限制而被系统终止的概率非常低,但并非不可能。

值得注意的是,当用户在前台服务运行时滑离(swipes away )您的应用程序时,您的应用程序进程仍然存在,但您的 Activity 堆栈和任何其他面向用户的元素都会被吹走(get blown away)。因此,必须格外小心以确保您的 BLE 逻辑存在于这些实体之外,就像在一个名为 ConnectionManager 的单例中处理应用程序的所有 BLE 需求一样。

通常,您的Activities和Fragments 不应该对 BLE 连接的状态做出任何假设。相反,他们应该依赖 ConnectionManager(或任何管理应用程序 BLE 需求的实体)作为唯一的事实来源,并根据 UI 的需要在 onCreate() 和 onResume() 中相应地更新 UI。

使用前台服务也不是完全安全的,因为 Android P 及更高版本具有自适应电池功能,有时会限制运行前台服务的应用程序进程的正常运行时间,但根据我们的经验,这似乎是随机发生的。到目前为止,解决此限制的唯一方法是要求您的用户关闭应用程序的电池优化,这是隐藏在“设置”应用程序中的一个选项。

其他 Android 后台处理技术,如依赖 AlarmManager 和 WorkManager 也是可行的,但连接事件不会是即时的,并且可能会丢失一些 BLE 通知或指示,如果这些事件发生时应用程序碰巧没有运行。
也就是说,如果您的应用程序不需要在事件发生时了解它们,并且可以定期唤醒,再次连接到 BLE 设备(如果它在附近),并查询新的数据, 那么 这些后台技术就是好的,可以接受的。

BluetoothManager getConnectionState(android.bluetooth.BluetoothDevice, int)

public int getConnectionState (BluetoothDevice device, int profile)

得到 远程设备profile的 连接状态
profile 为 GATT 或者 GATT_SERVER

返回值为:
STATE_DISCONNECTED = 0
STATE_CONNECTING = 1
STATE_CONNECTED = 2
STATE_DISCONNECTING = 3

BluetoothAdapter.getProfileConnectionState(int profile)
profile只能为 BluetoothProfile#HEADSET 或者 BluetoothProfile#A2DP.

    @SuppressLint("MissingPermission")
    public boolean isConnected() {
        if(mBluetoothGatt != null) {
             int state = mBluetoothManager.getConnectionState(mBluetoothGatt.getDevice(),  BluetoothProfile.GATT);
             return state == BluetoothProfile.STATE_CONNECTED;
        }
        return false;
    }

Android Activity requestPermissions

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/Activity.java

        if (mHasCurrentPermissionsRequest) {
            Log.w(TAG, "Can request only one set of permissions at a time");
            // Dispatch the callback with empty arrays which means a cancellation.
            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }

崩溃的地方, 一次只能请求一个权限集合

android native线程调用jni找不到Java Class

线程

所有线程(包括Java线程)都是 Linux 线程,由内核调度。

线程通常从受管理代码(Java代码)启动(使用 Thread.start()),但也可以在其他位置创建,然后附加到 JavaVM。

例如,可以使用 AttachCurrentThread() 或 AttachCurrentThreadAsDaemon() 函数附加通过 pthread_create() 或 std::thread 启动的线程。在附加之前,线程不包含任何 JNIEnv,也无法调用 JNI。

通常,最好使用 Thread.start() 创建需要调用 Java 代码的任何线程

这样做可以确保您有足够的堆栈空间、属于正确的 ThreadGroup 且与您的 Java 代码使用相同的 ClassLoader

而且,设置线程名称以在 Java 中进行调试也比通过原生代码更容易(如果您有 pthread_t 或 thread_t,请参阅 pthread_setname_np();如果您有 std::thread 且需要 pthread_t,请参阅 std::thread::native_handle())。

附加原生创建的线程会构建 java.lang.Thread 对象并将其添加到“主”ThreadGroup,从而使调试程序能够看到它。在已附加的线程上调用 AttachCurrentThread() 属于空操作。

Android 不会挂起执行原生代码的线程。如果正在进行垃圾回收,或者调试程序已发出挂起请求,则在线程下次调用 JNI 时,Android 会将其挂起。

通过 JNI 附加的线程在退出之前必须调用 DetachCurrentThread()。如果直接对此进行编码会很棘手,在 Android 2.0 (Eclair) 及更高版本中,您可以先使用 pthread_key_create() 定义将在线程退出之前调用的析构函数,之后再调用 DetachCurrentThread()。(将该键与 pthread_setspecific() 搭配使用,将 JNIEnv 存储在线程本地存储中;这样一来,该键将作为参数传递到您的析构函数中。)

===========
运行时可以通过两种方式找到您的原生方法。您可以使用 RegisterNatives 显示注册原生方法,也可以让运行时使用 dlsym 进行动态查找。RegisterNatives 的优势在于,您可以预先检查符号是否存在,而且还可以通过只导出 JNI_OnLoad 来获得规模更小、速度更快的共享库。
让运行时发现函数的优势在于,要编写的代码稍微少一些。

如需使用 RegisterNatives,请执行以下操作:
1) 提供 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) 函数。
2) 在 JNI_OnLoad 中,使用 RegisterNatives 注册所有原生方法。
3) 使用 -fvisibility=hidden 进行构建,以便只从您的库中导出您的 JNI_OnLoad。这将生成速度更快且规模更小的代码,并避免与加载到应用中的其他库发生潜在冲突(但如果应用在原生代码中崩溃,则创建的堆栈轨迹用处不大)。

从 JNI_OnLoad 进行的任何 FindClass 调用都会在用于加载共享库的类加载器的上下文中解析类。
从其他上下文调用时,FindClass 会使用与 Java 堆栈顶部的方法相关联的类加载器,如果没有(因为调用来自刚刚附加的原生线程),则会使用“系统”类加载器。

由于系统类加载器不知道应用的类,因此您将无法在该上下文中使用 FindClass 查找您自己的类

这使得 JNI_OnLoad 成为查找和缓存类的便捷位置:一旦有了有效的 jclass,您就可以从任何附加的线程使用它。

==============
常见问题解答:为什么 FindClass 找不到我的类?

如果类名称形式正确,则可能是您遇到了类加载器问题。FindClass 需要在与您的代码关联的类加载器中启动类搜索。它会检查调用堆栈,如下所示:

    Foo.myfunc(Native Method)
        Foo.main(Foo.java:10)

最顶层的方法是 Foo.myfunc。
FindClass 会查找与 Foo 类关联的 ClassLoader 对象并使用它。

采用这种方法通常会完成您想要执行的操作。如果您自行创建线程(可能通过调用 pthread_create,然后使用 AttachCurrentThread 进行附加),可能会遇到麻烦。现在您的应用中没有堆栈帧。如果从此线程调用 FindClass,JavaVM 会在“系统”类加载器(而不是与应用关联的类加载器)中启动,因此尝试查找特定于应用的类将失败。

您可以通过以下几种方法来解决此问题:

1. 在 JNI_OnLoad 中执行一次 FindClass 查找,然后缓存类引用以供日后使用。在执行 JNI_OnLoad 过程中发出的任何 FindClass 调用都会使用与调用 System.loadLibrary 的函数关联的类加载器(这是一条特殊规则,用于更方便地进行库初始化)。如果您的应用代码要加载库,FindClass 会使用正确的类加载器。

2. 通过声明原生方法来获取 Class 参数,然后传入 Foo.class,从而将类的实例传递给需要它的函数。

3. 在某个便捷位置缓存对 ClassLoader 对象的引用,然后直接发出 loadClass 调用。这需要花费一些精力来完成。