Update v0.6.2 for anylink

This commit is contained in:
Stille 2021-08-26 23:09:52 +08:00
parent 1489a5d11a
commit 3725ecf32f
61 changed files with 9727 additions and 6966 deletions

View File

@ -0,0 +1,71 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ dev ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ dev ]
schedule:
- cron: '32 12 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go', 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,24 +1,23 @@
# web
FROM node:lts-alpine as builder_node
ENV VERSION 0.5.1
ENV VERSION 0.6.2
WORKDIR /web
COPY ./web /web
RUN npx browserslist@latest --update-db \
&& npm install \
RUN npm install --registry=https://registry.npm.taobao.org \
&& npm run build \
&& ls /web/ui
# server
FROM golang:1.16-alpine as builder_golang
#TODO 本地打包时使用镜像
#ENV GOPROXY=https://goproxy.io
ENV GOPROXY=https://goproxy.io
ENV GOOS=linux
WORKDIR /anylink
COPY . /anylink
COPY --from=builder_node /web/ui /anylink/server/ui
#TODO 本地打包时使用镜像
#RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache git gcc musl-dev
RUN cd /anylink/server;go build -o anylink -ldflags "-X main.CommitId=$(git rev-parse HEAD)" \
&& /anylink/server/anylink tool -v
@ -33,12 +32,13 @@ WORKDIR /app
COPY --from=builder_golang /anylink/server/anylink /app/
COPY docker_entrypoint.sh /app/
COPY ./server/bridge-init.sh /app/
COPY ./server/conf /app/conf
COPY ./server/files /app/conf/files
#COPY ./server/files /app/conf/files
#TODO 本地打包时使用镜像
#RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk add --no-cache bash iptables \
&& chmod +x /app/docker_entrypoint.sh \
&& ls /app

17
anylink/doc/README.md Normal file
View File

@ -0,0 +1,17 @@
## Donate
> 如果您觉得 AnyLink 对你有帮助,欢迎给我们打赏,也是帮助 AnyLink 更好的发展。
<p>
<img src="screenshot/wxpay2.png" width="500" />
</p>
## Donator
> 感谢以下同学的打赏AnyLink 有你更美好!
| 昵称 | 主页 |
| -------- | ---------------------------- |
| 代码oo8 | |
| 甘磊 | https://github.com/ganlei333 |
| Oo@ | https://github.com/chooop |

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View File

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

13
anylink/docker_build.sh Normal file
View File

@ -0,0 +1,13 @@
#!/bin/env bash
ver="0.5.1"
#docker login -u bjdgyc
docker build -t bjdgyc/anylink .
docker tag bjdgyc/anylink:latest bjdgyc/anylink:$ver
docker push bjdgyc/anylink:$ver
docker push bjdgyc/anylink:latest

View File

@ -0,0 +1,33 @@
package admin
import (
"net/http"
"strconv"
"github.com/bjdgyc/anylink/dbdata"
)
func SetAuditList(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
pageS := r.FormValue("page")
page, _ := strconv.Atoi(pageS)
if page < 1 {
page = 1
}
var datas []dbdata.AccessAudit
count := dbdata.CountAll(&dbdata.AccessAudit{})
err := dbdata.Find(&datas, dbdata.PageSize, page)
if err != nil && !dbdata.CheckErrNotFound(err) {
RespError(w, RespInternalErr, err)
return
}
data := map[string]interface{}{
"count": count,
"page_size": dbdata.PageSize,
"datas": datas,
}
RespSucess(w, data)
}

View File

@ -7,7 +7,7 @@ import (
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/dgrijalva/jwt-go"
"github.com/golang-jwt/jwt/v4"
mail "github.com/xhit/go-simple-mail/v2"
// "github.com/mojocn/base64Captcha"
)

View File

@ -32,6 +32,7 @@ func StartAdmin() {
r.HandleFunc("/set/other/edit", SetOtherEdit)
r.HandleFunc("/set/other/smtp", SetOtherSmtp)
r.HandleFunc("/set/other/smtp/edit", SetOtherSmtpEdit)
r.HandleFunc("/set/audit/list", SetAuditList)
r.HandleFunc("/user/list", UserList)
r.HandleFunc("/user/detail", UserDetail)

View File

@ -2,6 +2,6 @@ package base
const (
APP_NAME = "AnyLink"
// 修改为sql数据库
APP_VER = "0.5.1"
// 修复前端bug
APP_VER = "0.6.2"
)

View File

@ -8,8 +8,10 @@ import (
)
const (
LinkModeTUN = "tun"
LinkModeTAP = "tap"
LinkModeTUN = "tun"
LinkModeTAP = "tap"
LinkModeMacvtap = "macvtap"
LinkModeIpvtap = "ipvtap"
)
var (
@ -48,11 +50,12 @@ type ServerConfig struct {
AdminPass string `json:"admin_pass"`
JwtSecret string `json:"jwt_secret"`
LinkMode string `json:"link_mode"` // tun tap
Ipv4CIDR string `json:"ipv4_cidr"` // 192.168.1.0/24
Ipv4Gateway string `json:"ipv4_gateway"`
Ipv4Start string `json:"ipv4_start"` // 192.168.1.100
Ipv4End string `json:"ipv4_end"` // 192.168.1.200
LinkMode string `json:"link_mode"` // tun tap macvtap ipvtap
Ipv4Master string `json:"ipv4_master"` // eth0
Ipv4CIDR string `json:"ipv4_cidr"` // 192.168.10.0/24
Ipv4Gateway string `json:"ipv4_gateway"` // 192.168.10.1
Ipv4Start string `json:"ipv4_start"` // 192.168.10.100
Ipv4End string `json:"ipv4_end"` // 192.168.10.200
IpLease int `json:"ip_lease"`
MaxClient int `json:"max_client"`
@ -65,6 +68,7 @@ type ServerConfig struct {
SessionTimeout int `json:"session_timeout"` // in seconds
// AuthTimeout int `json:"auth_timeout"` // in seconds
AuditInterval int `json:"audit_interval"` // in seconds
}
func initServerCfg() {

View File

@ -51,8 +51,8 @@ func execute() {
}
rr := ee.MapRange()
for rr.Next() {
// fmt.Println(rr.Key(), rr.Value())
envs[rr.Key().String()] = rr.Value().String()
// fmt.Println(rr.Key(), rr.Value().Index(0))
envs[rr.Key().String()] = rr.Value().Index(0).String()
}
if !runSrv {

View File

@ -26,19 +26,20 @@ var configs = []config{
{Typ: cfgStr, Name: "server_dtls_addr", Usage: "DTLS监听地址", ValStr: ":4433"},
{Typ: cfgStr, Name: "admin_addr", Usage: "后台服务监听地址", ValStr: ":8800"},
{Typ: cfgBool, Name: "proxy_protocol", Usage: "TCP代理协议", ValBool: false},
{Typ: cfgStr, Name: "db_type", Usage: "数据库类型 [sqlite3、mysql、postgres]", ValStr: "sqlite3"},
{Typ: cfgStr, Name: "db_type", Usage: "数据库类型 [sqlite3 mysql postgres]", ValStr: "sqlite3"},
{Typ: cfgStr, Name: "db_source", Usage: "数据库source", ValStr: "./conf/anylink.db"},
{Typ: cfgStr, Name: "cert_file", Usage: "证书文件", ValStr: "./conf/vpn_cert.pem"},
{Typ: cfgStr, Name: "cert_key", Usage: "证书密钥", ValStr: "./conf/vpn_cert.key"},
{Typ: cfgStr, Name: "files_path", Usage: "外部下载文件路径", ValStr: "./conf/files"},
{Typ: cfgStr, Name: "log_path", Usage: "日志文件路径,默认标准输出", ValStr: ""},
{Typ: cfgStr, Name: "log_level", Usage: "日志等级 [debug、info、warn、error]", ValStr: "info"},
{Typ: cfgStr, Name: "log_level", Usage: "日志等级 [debug info warn error]", ValStr: "info"},
{Typ: cfgBool, Name: "pprof", Usage: "开启pprof", ValBool: false},
{Typ: cfgStr, Name: "issuer", Usage: "系统名称", ValStr: "XX公司VPN"},
{Typ: cfgStr, Name: "admin_user", Usage: "管理用户名", ValStr: "admin"},
{Typ: cfgStr, Name: "admin_pass", Usage: "管理用户密码", ValStr: defaultPwd},
{Typ: cfgStr, Name: "jwt_secret", Usage: "JWT密钥", ValStr: defaultJwt},
{Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型", ValStr: "tun"},
{Typ: cfgStr, Name: "link_mode", Usage: "虚拟网络类型[tun tap macvtap ipvtap]", ValStr: "tun"},
{Typ: cfgStr, Name: "ipv4_master", Usage: "ipv4主网卡名称", ValStr: "eth0"},
{Typ: cfgStr, Name: "ipv4_cidr", Usage: "ip地址网段", ValStr: "192.168.10.0/24"},
{Typ: cfgStr, Name: "ipv4_gateway", Usage: "ipv4_gateway", ValStr: "192.168.10.1"},
{Typ: cfgStr, Name: "ipv4_start", Usage: "IPV4开始地址", ValStr: "192.168.10.100"},
@ -54,6 +55,7 @@ var configs = []config{
{Typ: cfgInt, Name: "mobile_dpd", Usage: "移动端死链接检测时间(秒)", ValInt: 60},
{Typ: cfgInt, Name: "session_timeout", Usage: "session过期时间(秒)", ValInt: 3600},
// {Typ: cfgInt, Name: "auth_timeout", Usage: "auth_timeout", ValInt: 0},
{Typ: cfgInt, Name: "audit_interval", Usage: "审计去重间隔(秒),-1关闭", ValInt: -1},
}
var envs = map[string]string{}

View File

@ -1,19 +1,13 @@
#!/bin/bash
#################################
# Set up Ethernet bridge on Linux
# Requires: bridge-utils
#################################
#yum install bridge-utils
# Define Bridge Interface
br="anylink0"
# Define physical ethernet interface to be bridged
# with TAP interface(s) above.
# 请根据sever服务器信息更新下面的信息
eth="eth0"
eth_ip="192.168.10.4"
eth_netmask="255.255.255.0"
eth_ip="192.168.10.4/24"
eth_broadcast="192.168.10.255"
eth_gateway="192.168.10.1"
@ -21,11 +15,14 @@ eth_gateway="192.168.10.1"
brctl addbr $br
brctl addif $br $eth
ifconfig $eth 0.0.0.0 up
ip addr del $eth_ip dev $eth
ip addr add 0.0.0.0 dev $eth
ip link set dev $eth up promisc on
mac=`cat /sys/class/net/$eth/address`
ifconfig $br hw ether $mac
ifconfig $br $eth_ip netmask $eth_netmask broadcast $eth_broadcast up
ip link set dev $br up address $mac promisc on
ip addr add $eth_ip broadcast $eth_broadcast dev $br
route add default gateway $eth_gateway

View File

@ -38,6 +38,7 @@ proxy_protocol = false
link_mode = "tun"
#客户端分配的ip地址池
ipv4_master = "eth0"
ipv4_cidr = "192.168.10.0/24"
ipv4_gateway = "192.168.10.1"
ipv4_start = "192.168.10.100"
@ -61,7 +62,7 @@ mobile_dpd = 50
#session过期时间用于断线重连0永不过期
session_timeout = 3600
auth_timeout = 0
audit_interval = -1

View File

@ -25,7 +25,7 @@ func initDb() {
}
// 初始化数据库
err = xdb.Sync2(&User{}, &Setting{}, &Group{}, &IpMap{})
err = xdb.Sync2(&User{}, &Setting{}, &Group{}, &IpMap{}, &AccessAudit{})
if err != nil {
base.Fatal(err)
}

View File

@ -68,17 +68,26 @@ func SetGroup(g *Group) error {
clientDns := []ValData{}
for _, v := range g.ClientDns {
if v.Val != "" {
ip := net.ParseIP(v.Val)
if ip.String() != v.Val {
return errors.New("DNS IP 错误")
}
clientDns = append(clientDns, v)
}
}
if len(clientDns) == 0 {
return errors.New("DNS 错误")
return errors.New("必须设置一个DNS")
}
g.ClientDns = clientDns
routeInclude := []ValData{}
for _, v := range g.RouteInclude {
if v.Val != "" {
if v.Val == "all" {
routeInclude = append(routeInclude, v)
continue
}
ipMask, _, err := parseIpNet(v.Val)
if err != nil {
return errors.New("RouteInclude 错误" + err.Error())

View File

@ -54,3 +54,14 @@ type Setting struct {
Data json.RawMessage `json:"data" xorm:"Text"`
UpdatedAt time.Time `json:"updated_at" xorm:"DateTime updated"`
}
type AccessAudit struct {
Id int `json:"id" xorm:"pk autoincr not null"`
Username string `json:"username" xorm:"varchar(60) not null"`
Protocol uint8 `json:"protocol" xorm:"not null"`
Src string `json:"src" xorm:"varchar(60) not null"`
SrcPort uint16 `json:"src_port" xorm:"not null"`
Dst string `json:"dst" xorm:"varchar(60) not null"`
DstPort uint16 `json:"dst_port" xorm:"not null"`
CreatedAt time.Time `json:"created_at" xorm:"DateTime"`
}

View File

@ -3,32 +3,29 @@ module github.com/bjdgyc/anylink
go 1.16
require (
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/go-sql-driver/mysql v1.6.0
github.com/golang/snappy v0.0.1 // indirect
github.com/golang-jwt/jwt/v4 v4.0.0
github.com/google/gopacket v1.1.19
github.com/gorilla/mux v1.8.0
github.com/lib/pq v1.7.0
github.com/mattn/go-sqlite3 v1.14.6
github.com/pion/dtls/v2 v2.0.0-00010101000000-000000000000
github.com/lib/pq v1.10.2
github.com/mattn/go-sqlite3 v1.14.8
github.com/pion/dtls/v2 v2.0.9
github.com/pion/logging v0.2.2
github.com/shirou/gopsutil v3.21.4+incompatible
github.com/shirou/gopsutil v3.21.7+incompatible
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/songgao/packets v0.0.0-20160404182456-549a10cd4091
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/cobra v1.1.3
github.com/spf13/viper v1.7.1
github.com/spf13/cobra v1.2.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tklauser/go-sysconf v0.3.6 // indirect
github.com/xhit/go-simple-mail/v2 v2.9.0
github.com/tklauser/go-sysconf v0.3.7 // indirect
github.com/xhit/go-simple-mail/v2 v2.10.0
github.com/xlzd/gotp v0.0.0-20181030022105-c8557ba2c119
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
xorm.io/xorm v1.1.2
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac
xorm.io/xorm v1.2.2
)
replace github.com/pion/dtls/v2 => ../dtls-2.0.9

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,17 @@
package handler
import (
"bufio"
"encoding/binary"
"net"
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
)
func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
func LinkCstp(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("LinkCstp return", cSess.IpAddr)
_ = conn.Close()
@ -23,19 +25,19 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
dead = time.Duration(cSess.CstpDpd+5) * time.Second
)
go cstpWrite(conn, cSess)
go cstpWrite(conn, bufRW, cSess)
for {
// 设置超时限制
err = conn.SetReadDeadline(time.Now().Add(dead))
err = conn.SetReadDeadline(utils.NowSec().Add(dead))
if err != nil {
base.Error("SetDeadline: ", err)
return
}
// hdata := make([]byte, BufferSize)
pl := getPayload()
n, err = conn.Read(pl.Data)
n, err = bufRW.Read(pl.Data)
if err != nil {
base.Error("read hdata: ", err)
return
@ -77,7 +79,7 @@ func LinkCstp(conn net.Conn, cSess *sessdata.ConnSession) {
}
}
func cstpWrite(conn net.Conn, cSess *sessdata.ConnSession) {
func cstpWrite(conn net.Conn, bufRW *bufio.ReadWriter, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("cstpWrite return", cSess.IpAddr)
_ = conn.Close()

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
)
@ -32,7 +33,7 @@ func LinkDtls(conn net.Conn, cSess *sessdata.ConnSession) {
go dtlsWrite(conn, dSess, cSess)
for {
err = conn.SetReadDeadline(time.Now().Add(dead))
err = conn.SetReadDeadline(utils.NowSec().Add(dead))
if err != nil {
base.Error("SetDeadline: ", err)
return

View File

@ -2,6 +2,7 @@ package handler
import (
"fmt"
"io"
"net"
"github.com/bjdgyc/anylink/base"
@ -17,18 +18,32 @@ import (
const bridgeName = "anylink0"
var (
bridgeIp net.IP
bridgeHw net.HardwareAddr
// 网关mac地址
gatewayHw net.HardwareAddr
)
func checkTap() {
brFace, err := net.InterfaceByName(bridgeName)
type LinkDriver interface {
io.ReadWriteCloser
Name() string
}
func _setGateway() {
dstAddr := arpdis.Lookup(sessdata.IpPool.Ipv4Gateway, false)
gatewayHw = dstAddr.HardwareAddr
// 设置为静态地址映射
dstAddr.Type = arpdis.TypeStatic
arpdis.Add(dstAddr)
}
func _checkTapIp(ifName string) {
iFace, err := net.InterfaceByName(ifName)
if err != nil {
base.Fatal("testTap err: ", err)
}
bridgeHw = brFace.HardwareAddr
addrs, err := brFace.Addrs()
var ifIp net.IP
addrs, err := iFace.Addrs()
if err != nil {
base.Fatal("testTap err: ", err)
}
@ -37,17 +52,19 @@ func checkTap() {
if err != nil || ip.To4() == nil {
continue
}
bridgeIp = ip
}
if bridgeIp == nil && bridgeHw == nil {
base.Fatal("bridgeIp is err")
ifIp = ip
}
if !sessdata.IpPool.Ipv4IPNet.Contains(bridgeIp) {
base.Fatal("bridgeIp or Ip network err")
if !sessdata.IpPool.Ipv4IPNet.Contains(ifIp) {
base.Fatal("tapIp or Ip network err")
}
}
func checkTap() {
_setGateway()
_checkTapIp(bridgeName)
}
// 创建tap网卡
func LinkTap(cSess *sessdata.ConnSession) error {
cfg := water.Config{
@ -60,9 +77,8 @@ func LinkTap(cSess *sessdata.ConnSession) error {
return err
}
cSess.TunName = ifce.Name()
cSess.SetIfName(ifce.Name())
// arp on
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast on", ifce.Name(), cSess.Mtu)
cmdstr2 := fmt.Sprintf("ip link set dev %s master %s", ifce.Name(), bridgeName)
err = execCmd([]string{cmdstr1, cmdstr2})
@ -75,25 +91,31 @@ func LinkTap(cSess *sessdata.ConnSession) error {
cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifce.Name())
execCmd([]string{cmdstr3})
go tapRead(ifce, cSess)
go tapWrite(ifce, cSess)
go allTapRead(ifce, cSess)
go allTapWrite(ifce, cSess)
return nil
}
func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
// ========================通用代码===========================
func allTapWrite(ifce LinkDriver, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("LinkTap return", cSess.IpAddr)
cSess.Close()
_ = ifce.Close()
ifce.Close()
}()
var (
err error
dstHw net.HardwareAddr
pl *sessdata.Payload
frame ethernet.Frame
frame = make(ethernet.Frame, BufferSize)
ipDst = net.IPv4(1, 2, 3, 4)
)
for {
frame.Resize(BufferSize)
select {
case pl = <-cSess.PayloadIn:
case <-cSess.CloseChan:
@ -101,45 +123,46 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
}
// var frame ethernet.Frame
fb := getByteFull()
frame = *fb
switch pl.LType {
default:
// log.Println(payload)
case sessdata.LTypeEthernet:
copy(frame, pl.Data)
frame = frame[:len(pl.Data)]
// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
// fmt.Println("wirteArp:", packet)
case sessdata.LTypeIPData: // 需要转换成 Ethernet 数据
ip_src := waterutil.IPv4Source(pl.Data)
if waterutil.IsIPv6(pl.Data) || !ip_src.Equal(cSess.IpAddr) {
// 过滤掉IPv6的数据
ipSrc := waterutil.IPv4Source(pl.Data)
if !ipSrc.Equal(cSess.IpAddr) {
// 非分配给客户端ip直接丢弃
continue
}
// packet := gopacket.NewPacket(data, layers.LayerTypeIPv4, gopacket.Default)
if waterutil.IsIPv6(pl.Data) {
// 过滤掉IPv6的数据
continue
}
// packet := gopacket.NewPacket(pl.Data, layers.LayerTypeIPv4, gopacket.Default)
// fmt.Println("get:", packet)
ip_dst := waterutil.IPv4Destination(pl.Data)
// fmt.Println("get:", ip_src, ip_dst)
// 手动设置ipv4地址
ipDst[12] = pl.Data[16]
ipDst[13] = pl.Data[17]
ipDst[14] = pl.Data[18]
ipDst[15] = pl.Data[19]
var dstHw net.HardwareAddr
if !sessdata.IpPool.Ipv4IPNet.Contains(ip_dst) || ip_dst.Equal(sessdata.IpPool.Ipv4Gateway) {
// 不是同一网段使用网关mac地址
dstAddr := arpdis.Lookup(sessdata.IpPool.Ipv4Gateway, false)
dstHw = dstAddr.HardwareAddr
} else {
dstAddr := arpdis.Lookup(ip_dst, true)
dstHw = gatewayHw
if sessdata.IpPool.Ipv4IPNet.Contains(ipDst) {
dstAddr := arpdis.Lookup(ipDst, true)
// fmt.Println("dstAddr", dstAddr)
if dstAddr != nil {
dstHw = dstAddr.HardwareAddr
} else {
dstHw = bridgeHw
}
}
// fmt.Println("Gateway", ip_dst, dstAddr.HardwareAddr)
// fmt.Println("Gateway", ipSrc, ipDst, dstHw)
frame.Prepare(dstHw, cSess.MacHw, ethernet.NotTagged, ethernet.IPv4, len(pl.Data))
copy(frame[12+2:], pl.Data)
}
@ -152,29 +175,26 @@ func tapWrite(ifce *water.Interface, cSess *sessdata.ConnSession) {
return
}
putByte(fb)
putPayload(pl)
}
}
func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
func allTapRead(ifce LinkDriver, cSess *sessdata.ConnSession) {
defer func() {
base.Debug("tapRead return", cSess.IpAddr)
_ = ifce.Close()
ifce.Close()
}()
var (
err error
n int
data []byte
frame ethernet.Frame
frame = make(ethernet.Frame, BufferSize)
)
for {
// var frame ethernet.Frame
// frame.Resize(BufferSize)
fb := getByteFull()
frame = *fb
frame.Resize(BufferSize)
n, err = ifce.Read(frame)
if err != nil {
base.Error("tap Read err", n, err)
@ -184,8 +204,6 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
switch frame.Ethertype() {
default:
// packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
// fmt.Println(packet)
continue
case ethernet.IPv6:
continue
@ -214,7 +232,7 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
case ethernet.ARP:
// 暂时仅实现了ARP协议
packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.Default)
packet := gopacket.NewPacket(frame, layers.LayerTypeEthernet, gopacket.NoCopy)
layer := packet.Layer(layers.LayerTypeARP)
arpReq := layer.(*layers.ARP)
@ -223,12 +241,12 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
continue
}
// fmt.Println("arp", net.IP(arpReq.SourceProtAddress), sess.Ip)
// fmt.Println("arp", time.Now(), net.IP(arpReq.SourceProtAddress), cSess.IpAddr)
// fmt.Println(packet)
// 返回ARP数据
src := &arpdis.Addr{IP: cSess.IpAddr, HardwareAddr: cSess.MacHw}
dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: frame.Source()}
dst := &arpdis.Addr{IP: arpReq.SourceProtAddress, HardwareAddr: arpReq.SourceHwAddress}
data, err = arpdis.NewARPReply(src, dst)
if err != nil {
base.Error(err)
@ -237,13 +255,9 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
// 从接受的arp信息添加arp地址
addr := &arpdis.Addr{
IP: make([]byte, len(arpReq.SourceProtAddress)),
HardwareAddr: make([]byte, len(frame.Source())),
IP: append([]byte{}, dst.IP...),
HardwareAddr: append([]byte{}, dst.HardwareAddr...),
}
// addr.IP = arpReq.SourceProtAddress
// addr.HardwareAddr = frame.Source()
copy(addr.IP, arpReq.SourceProtAddress)
copy(addr.HardwareAddr, frame.Source())
arpdis.Add(addr)
pl := getPayload()
@ -259,7 +273,5 @@ func tapRead(ifce *water.Interface, cSess *sessdata.ConnSession) {
}
}
putByte(fb)
}
}

View File

@ -40,8 +40,7 @@ func LinkTun(cSess *sessdata.ConnSession) error {
return err
}
// log.Printf("Interface Name: %s\n", ifce.Name())
cSess.SetTunName(ifce.Name())
// cSess.TunName = ifce.Name()
cSess.SetIfName(ifce.Name())
cmdstr1 := fmt.Sprintf("ip link set dev %s up mtu %d multicast off", ifce.Name(), cSess.Mtu)
cmdstr2 := fmt.Sprintf("ip addr add dev %s local %s peer %s/32",

View File

@ -96,6 +96,9 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
}
// 允许的路由
for _, v := range cSess.Group.RouteInclude {
if v.Val == "all" {
continue
}
w.Header().Add("X-CSTP-Split-Include", v.IpMask)
}
// 不允许的路由
@ -147,7 +150,7 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
base.Debug(buf.String())
hj := w.(http.Hijacker)
conn, _, err := hj.Hijack()
conn, bufRW, err := hj.Hijack()
if err != nil {
base.Error(err)
w.WriteHeader(http.StatusInternalServerError)
@ -160,11 +163,14 @@ func LinkTunnel(w http.ResponseWriter, r *http.Request) {
err = LinkTun(cSess)
case base.LinkModeTAP:
err = LinkTap(cSess)
case base.LinkModeMacvtap:
err = LinkMacvtap(cSess)
}
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
conn.Close()
base.Error(err)
return
}
go LinkCstp(conn, cSess)
go LinkCstp(conn, bufRW, cSess)
}

View File

@ -0,0 +1,137 @@
package handler
import (
"fmt"
"net"
"os"
"strings"
"syscall"
"unsafe"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
)
// link vtap
const vTapPrefix = "lvtap"
type Vtap struct {
*os.File
ifName string
}
func (v *Vtap) Close() error {
v.File.Close()
cmdstr := fmt.Sprintf("ip link del %s", v.ifName)
return execCmd([]string{cmdstr})
}
func checkMacvtap() {
_setGateway()
_checkTapIp(base.Cfg.Ipv4Master)
ifName := "anylinkMacvtap"
// 加载 macvtap
cmdstr0 := fmt.Sprintf("modprobe -i macvtap")
// 开启主网卡混杂模式
cmdstr1 := fmt.Sprintf("ip link set dev %s promisc on", base.Cfg.Ipv4Master)
// 测试 macvtap 功能
cmdstr2 := fmt.Sprintf("ip link add link %s name %s type macvtap mode bridge", base.Cfg.Ipv4Master, ifName)
cmdstr3 := fmt.Sprintf("ip link del %s", ifName)
err := execCmd([]string{cmdstr0, cmdstr1, cmdstr2, cmdstr3})
if err != nil {
base.Fatal(err)
}
}
// 创建 Macvtap 网卡
func LinkMacvtap(cSess *sessdata.ConnSession) error {
capL := sessdata.IpPool.IpLongMax - sessdata.IpPool.IpLongMin
ipN := utils.Ip2long(cSess.IpAddr) % capL
ifName := fmt.Sprintf("%s%d", vTapPrefix, ipN)
cSess.SetIfName(ifName)
cmdstr1 := fmt.Sprintf("ip link add link %s name %s type macvtap mode bridge", base.Cfg.Ipv4Master, ifName)
cmdstr2 := fmt.Sprintf("ip link set dev %s up mtu %d address %s", ifName, cSess.Mtu, cSess.MacHw)
err := execCmd([]string{cmdstr1, cmdstr2})
if err != nil {
base.Error(err)
return err
}
cmdstr3 := fmt.Sprintf("sysctl -w net.ipv6.conf.%s.disable_ipv6=1", ifName)
execCmd([]string{cmdstr3})
return createVtap(cSess, ifName)
}
func checkIpvtap() {
}
// 创建 Ipvtap 网卡
func LinkIpvtap(cSess *sessdata.ConnSession) error {
return nil
}
type ifReq struct {
Name [0x10]byte
Flags uint16
pad [0x28 - 0x10 - 2]byte
}
func createVtap(cSess *sessdata.ConnSession, ifName string) error {
// 初始化 ifName
inf, err := net.InterfaceByName(ifName)
if err != nil {
base.Error(err)
return err
}
tName := fmt.Sprintf("/dev/tap%d", inf.Index)
var fdInt int
fdInt, err = syscall.Open(tName, syscall.O_RDWR|syscall.O_NONBLOCK, 0)
if err != nil {
return err
}
var flags uint16 = syscall.IFF_TAP | syscall.IFF_NO_PI
var req ifReq
req.Flags = flags
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
uintptr(fdInt),
uintptr(syscall.TUNSETIFF),
uintptr(unsafe.Pointer(&req)),
)
if errno != 0 {
return os.NewSyscallError("ioctl", errno)
}
file := os.NewFile(uintptr(fdInt), tName)
ifce := &Vtap{file, ifName}
go allTapRead(ifce, cSess)
go allTapWrite(ifce, cSess)
return nil
}
// 销毁未关闭的vtap
func destroyVtap() {
its, err := net.Interfaces()
if err != nil {
base.Error(err)
return
}
for _, v := range its {
if strings.HasPrefix(v.Name, vTapPrefix) {
// 删除原来的网卡
cmdstr := fmt.Sprintf("ip link del %s", v.Name)
execCmd([]string{cmdstr})
}
}
}

View File

@ -1,17 +1,25 @@
package handler
import (
"encoding/binary"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/pkg/utils"
"github.com/bjdgyc/anylink/sessdata"
"github.com/songgao/water/waterutil"
)
func payloadIn(cSess *sessdata.ConnSession, pl *sessdata.Payload) bool {
// 进行Acl规则判断
check := checkLinkAcl(cSess.Group, pl)
if !check {
// 校验不通过直接丢弃
return false
if pl.LType == sessdata.LTypeIPData && pl.PType == 0x00 {
// 进行Acl规则判断
check := checkLinkAcl(cSess.Group, pl)
if !check {
// 校验不通过直接丢弃
return false
}
logAudit(cSess, pl)
}
closed := false
@ -61,24 +69,23 @@ func checkLinkAcl(group *dbdata.Group, pl *sessdata.Payload) bool {
return true
}
data := pl.Data
ip_dst := waterutil.IPv4Destination(data)
ip_port := waterutil.IPv4DestinationPort(data)
ip_proto := waterutil.IPv4Protocol(data)
ipDst := waterutil.IPv4Destination(pl.Data)
ipPort := waterutil.IPv4DestinationPort(pl.Data)
ipProto := waterutil.IPv4Protocol(pl.Data)
// fmt.Println("sent:", ip_dst, ip_port)
// 优先放行dns端口
for _, v := range group.ClientDns {
if v.Val == ip_dst.String() && ip_port == 53 {
if v.Val == ipDst.String() && ipPort == 53 {
return true
}
}
for _, v := range group.LinkAcl {
// 循环判断ip和端口
if v.IpNet.Contains(ip_dst) {
if v.IpNet.Contains(ipDst) {
// 放行允许ip的ping
if v.Port == ip_port || v.Port == 0 || ip_proto == waterutil.ICMP {
if v.Port == ipPort || v.Port == 0 || ipProto == waterutil.ICMP {
if v.Action == dbdata.Allow {
return true
} else {
@ -90,3 +97,53 @@ func checkLinkAcl(group *dbdata.Group, pl *sessdata.Payload) bool {
return false
}
// 访问日志审计
func logAudit(cSess *sessdata.ConnSession, pl *sessdata.Payload) {
if base.Cfg.AuditInterval < 0 {
return
}
ipProto := waterutil.IPv4Protocol(pl.Data)
// 只统计 tcp和udp 的访问
switch ipProto {
case waterutil.TCP:
case waterutil.UDP:
default:
return
}
ipSrc := waterutil.IPv4Source(pl.Data)
ipDst := waterutil.IPv4Destination(pl.Data)
ipPort := waterutil.IPv4DestinationPort(pl.Data)
b := getByte34()
key := *b
copy(key[:16], ipSrc)
copy(key[16:32], ipDst)
binary.BigEndian.PutUint16(key[32:34], ipPort)
s := utils.BytesToString(key)
nu := utils.NowSec().Unix()
// 判断已经存在,并且没有过期
v, ok := cSess.IpAuditMap[s]
if ok && nu-v < int64(base.Cfg.AuditInterval) {
// 回收byte对象
putByte34(b)
return
}
cSess.IpAuditMap[s] = nu
audit := dbdata.AccessAudit{
Username: cSess.Sess.Username,
Protocol: uint8(ipProto),
Src: ipSrc.String(),
Dst: ipDst.String(),
DstPort: ipPort,
CreatedAt: utils.NowSec(),
}
_ = dbdata.Add(audit)
}

View File

@ -9,7 +9,12 @@ import (
// 不允许直接修改
// [6] => PType
var plHeader = []byte{'S', 'T', 'F', 0x01, 0x00, 0x00, 0x00, 0x00}
var plHeader = []byte{
'S', 'T', 'F', 1,
0x00, 0x00, /* Length */
0x00, /* Type */
0x00, /* Unknown */
}
var plPool = sync.Pool{
New: func() interface{} {
@ -64,3 +69,21 @@ func putByte(b *[]byte) {
*b = (*b)[:BufferSize]
bytePool.Put(b)
}
// 长度 34 小对象
var byte34Pool = sync.Pool{
New: func() interface{} {
b := make([]byte, 34)
return &b
},
}
func getByte34() *[]byte {
b := byte34Pool.Get().(*[]byte)
return b
}
func putByte34(b *[]byte) {
*b = (*b)[:34]
byte34Pool.Put(b)
}

View File

@ -10,11 +10,10 @@ import (
"os"
"time"
"github.com/pion/dtls/v2/pkg/crypto/selfsign"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/pkg/proxyproto"
"github.com/gorilla/mux"
"github.com/pion/dtls/v2/pkg/crypto/selfsign"
)
func startTls() {
@ -45,10 +44,10 @@ func startTls() {
// 设置tls信息
tlsConfig := &tls.Config{
NextProtos: []string{"http/1.1"},
MinVersion: tls.VersionTLS12,
InsecureSkipVerify: true,
Certificates: certs,
NextProtos: []string{"http/1.1"},
MinVersion: tls.VersionTLS12,
Certificates: certs,
// InsecureSkipVerify: true,
}
srv := &http.Server{
Addr: addr,

View File

@ -11,10 +11,17 @@ func Start() {
dbdata.Start()
sessdata.Start()
checkTun()
if base.Cfg.LinkMode == base.LinkModeTAP {
switch base.Cfg.LinkMode {
case base.LinkModeTUN:
checkTun()
case base.LinkModeTAP:
checkTap()
case base.LinkModeMacvtap:
checkMacvtap()
default:
base.Fatal("LinkMode is err")
}
go admin.StartAdmin()
go startTls()
go startDtls()
@ -22,4 +29,5 @@ func Start() {
func Stop() {
_ = dbdata.Stop()
destroyVtap()
}

View File

@ -11,7 +11,6 @@ import (
"syscall"
"github.com/bjdgyc/anylink/admin"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/handler"
)

View File

@ -4,6 +4,8 @@ import (
"net"
"sync"
"time"
"github.com/bjdgyc/anylink/pkg/utils"
)
const (
@ -16,7 +18,7 @@ const (
)
var (
table = make(map[string]*Addr)
table = make(map[string]*Addr, 128)
tableMu sync.RWMutex
)
@ -40,25 +42,25 @@ func Lookup(ip net.IP, onlyTable bool) *Addr {
// Add adds a IP-MAC map to a runtime ARP table.
func tableLookup(ip net.IP) *Addr {
tableMu.Lock()
tableMu.RLock()
addr := table[ip.To4().String()]
tableMu.Unlock()
tableMu.RUnlock()
if addr == nil {
return nil
}
// 判断老化过期时间
tsub := time.Since(addr.disTime)
tSub := utils.NowSec().Sub(addr.disTime)
switch addr.Type {
case TypeStatic:
case TypeNormal:
if tsub > StaleTimeNormal {
if tSub > StaleTimeNormal {
return nil
}
case TypeUnreachable:
if tsub > StaleTimeUnreachable {
if tSub > StaleTimeUnreachable {
return nil
}
case TypeStatic:
}
return addr
@ -70,7 +72,7 @@ func Add(addr *Addr) {
return
}
if addr.disTime.IsZero() {
addr.disTime = time.Now()
addr.disTime = utils.NowSec()
}
ip := addr.IP.To4().String()
tableMu.Lock()

View File

@ -19,7 +19,8 @@ func doLookup(ip net.IP) *Addr {
err := doPing(ip.String())
if err != nil {
// log.Println(err)
addr := &Addr{IP: ip, Type: TypeUnreachable}
addr := &Addr{IP: net.IPv4(1, 2, 3, 4), Type: TypeUnreachable}
copy(addr.IP, ip)
return addr
}
@ -50,7 +51,9 @@ func doArpShow(ip net.IP) *Addr {
return nil
}
return &Addr{IP: ip, HardwareAddr: mac}
addr := &Addr{IP: net.IPv4(1, 2, 3, 4), HardwareAddr: mac}
copy(addr.IP, ip)
return addr
}
// IP address HW type Flags HW address Mask Device

View File

@ -0,0 +1,17 @@
package utils
import (
"encoding/binary"
"net"
)
func Long2ip(i uint32) net.IP {
ip := make([]byte, 4)
binary.BigEndian.PutUint32(ip, i)
return ip
}
func Ip2long(ip net.IP) uint32 {
ip = ip.To4()
return binary.BigEndian.Uint32(ip)
}

View File

@ -0,0 +1,20 @@
package utils
import (
"unsafe"
)
// BytesToString converts byte slice to string.
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// StringToBytes converts string to byte slice.
func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(
&struct {
string
Cap int
}{s, len(s)},
))
}

View File

@ -3,11 +3,30 @@ package utils
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
var (
// 每秒时间缓存
timeNowSec = &atomic.Value{}
)
func init() {
rand.Seed(time.Now().UnixNano())
timeNowSec.Store(time.Now())
go func() {
tick := time.NewTicker(time.Second * 1)
for c := range tick.C {
timeNowSec.Store(c)
}
}()
}
func NowSec() time.Time {
t := timeNowSec.Load()
return t.(time.Time)
}
func InArrStr(arr []string, str string) bool {

View File

@ -1,13 +1,13 @@
package sessdata
import (
"encoding/binary"
"net"
"sync"
"time"
"github.com/bjdgyc/anylink/base"
"github.com/bjdgyc/anylink/dbdata"
"github.com/bjdgyc/anylink/pkg/utils"
)
var (
@ -43,19 +43,8 @@ func initIpPool() {
// max := min | uint32(math.Pow(2, float64(32-one))-1)
// ip地址池
IpPool.IpLongMin = ip2long(net.ParseIP(base.Cfg.Ipv4Start))
IpPool.IpLongMax = ip2long(net.ParseIP(base.Cfg.Ipv4End))
}
func long2ip(i uint32) net.IP {
ip := make([]byte, 4)
binary.BigEndian.PutUint32(ip, i)
return ip
}
func ip2long(ip net.IP) uint32 {
ip = ip.To4()
return binary.BigEndian.Uint32(ip)
IpPool.IpLongMin = utils.Ip2long(net.ParseIP(base.Cfg.Ipv4Start))
IpPool.IpLongMax = utils.Ip2long(net.ParseIP(base.Cfg.Ipv4End))
}
// AcquireIp 获取动态ip
@ -90,7 +79,7 @@ func AcquireIp(username, macAddr string) net.IP {
farIp := &dbdata.IpMap{LastLogin: tNow}
// 全局遍历超过租期ip
for i := IpPool.IpLongMin; i <= IpPool.IpLongMax; i++ {
ip := long2ip(i)
ip := utils.Long2ip(i)
ipStr := ip.String()
// 跳过活跃连接

View File

@ -54,13 +54,13 @@ func OnlineSess() []Online {
Group: v.Group,
MacAddr: v.MacAddr,
RemoteAddr: v.CSess.RemoteAddr,
TunName: v.CSess.TunName,
TunName: v.CSess.IfName,
Mtu: v.CSess.Mtu,
Client: v.CSess.Client,
BandwidthUp: utils.HumanByte(atomic.LoadUint32(&v.CSess.BandwidthUpPeriod)) + "/s",
BandwidthDown: utils.HumanByte(atomic.LoadUint32(&v.CSess.BandwidthDownPeriod)) + "/s",
BandwidthUpAll: utils.HumanByte(atomic.LoadUint32(&v.CSess.BandwidthUpAll)),
BandwidthDownAll: utils.HumanByte(atomic.LoadUint32(&v.CSess.BandwidthDownAll)),
BandwidthUpAll: utils.HumanByte(atomic.LoadUint64(&v.CSess.BandwidthUpAll)),
BandwidthDownAll: utils.HumanByte(atomic.LoadUint64(&v.CSess.BandwidthDownAll)),
LastLogin: v.LastLogin,
}
datas = append(datas, val)

View File

@ -32,7 +32,7 @@ type ConnSession struct {
MacHw net.HardwareAddr // 客户端mac地址,从Session取出
RemoteAddr string
Mtu int
TunName string
IfName string
Client string // 客户端 mobile pc
CstpDpd int
Group *dbdata.Group
@ -41,14 +41,14 @@ type ConnSession struct {
BandwidthDown uint32 // 使用下行带宽 Byte
BandwidthUpPeriod uint32 // 前一周期的总量
BandwidthDownPeriod uint32
BandwidthUpAll uint32 // 使用上行带宽总量
BandwidthDownAll uint32 // 使用下行带宽总量
BandwidthUpAll uint64 // 使用上行带宽总量
BandwidthDownAll uint64 // 使用下行带宽总量
closeOnce sync.Once
CloseChan chan struct{}
PayloadIn chan *Payload
// PayloadOut chan *Payload // 公共ip数据
PayloadOutCstp chan *Payload // Cstp的数据
PayloadOutDtls chan *Payload // Dtls的数据
PayloadOutCstp chan *Payload // Cstp的数据
PayloadOutDtls chan *Payload // Dtls的数据
IpAuditMap map[string]int64 // 审计的ip数据
// dSess *DtlsSession
dSess *atomic.Value
@ -111,9 +111,9 @@ func checkSession() {
func GenToken() string {
// 生成32位的 token
btoken := make([]byte, 32)
rand.Read(btoken)
return fmt.Sprintf("%x", btoken)
bToken := make([]byte, 32)
rand.Read(bToken)
return fmt.Sprintf("%x", bToken)
}
func NewSession(token string) *Session {
@ -189,6 +189,11 @@ func (s *Session) NewConn() *ConnSession {
dSess: &atomic.Value{},
}
// ip 审计
if base.Cfg.AuditInterval >= 0 {
cSess.IpAuditMap = make(map[string]int64, 512)
}
dSess := &DtlsSession{
isActive: -1,
}
@ -284,8 +289,8 @@ func (cs *ConnSession) ratePeriod() {
atomic.SwapUint32(&cs.BandwidthUpPeriod, rtUp/BandwidthPeriodSec)
atomic.SwapUint32(&cs.BandwidthDownPeriod, rtDown/BandwidthPeriodSec)
// 累加所有流量
atomic.AddUint32(&cs.BandwidthUpAll, rtUp)
atomic.AddUint32(&cs.BandwidthDownAll, rtDown)
atomic.AddUint64(&cs.BandwidthUpAll, uint64(rtUp))
atomic.AddUint64(&cs.BandwidthDownAll, uint64(rtDown))
}
}
@ -304,10 +309,10 @@ func (cs *ConnSession) SetMtu(mtu string) {
}
}
func (cs *ConnSession) SetTunName(name string) {
func (cs *ConnSession) SetIfName(name string) {
cs.Sess.mux.Lock()
defer cs.Sess.mux.Unlock()
cs.TunName = name
cs.IfName = name
}
func (cs *ConnSession) RateLimit(byt int, isUp bool) error {

View File

@ -1,13 +1,15 @@
[Unit]
Description=VPN Server Service
After=network.target
Description=AnyLink Server Service
Documentation=https://github.com/bjdgyc/anylink
After=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory= /usr/local/anylink-deploy
WorkingDirectory=/usr/local/anylink-deploy
Restart=on-failure
RestartSec=5s
ExecStart=/usr/local/anylink-deploy/anylink --conf=conf/server.toml
ExecStart=/usr/local/anylink-deploy/anylink --conf=./conf/server.toml
[Install]
WantedBy=multi-user.target

View File

@ -1,3 +0,0 @@
> 1%
last 2 versions
not dead

File diff suppressed because it is too large Load Diff

View File

@ -8,15 +8,13 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.20.0",
"caniuse-lite": "^1.0.30001242",
"chokidar": "^3.5.2",
"axios": "^0.21.1",
"core-js": "^3.6.5",
"echarts": "^4.9.0",
"element-ui": "^2.4.5",
"vue": "^2.6.11",
"vue-count-to": "^1.0.13",
"vue-router": "^3.4.6"
"vue-router": "^3.5.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
@ -27,5 +25,24 @@
"eslint-plugin-vue": "^6.2.2",
"vue-cli-plugin-element": "~1.0.1",
"vue-template-compiler": "^2.6.11"
}
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

View File

@ -32,6 +32,7 @@
<el-menu-item index="/admin/set/system">系统信息</el-menu-item>
<el-menu-item index="/admin/set/soft">软件配置</el-menu-item>
<el-menu-item index="/admin/set/other">其他设置</el-menu-item>
<el-menu-item index="/admin/set/audit">审计日志</el-menu-item>
</el-submenu>
<el-submenu index="2">

View File

@ -118,8 +118,8 @@
<el-popconfirm
style="margin-left: 10px"
@onConfirm="handleDel(scope.row)"
title="确定要删除用户吗?">
@confirm="handleDel(scope.row)"
title="确定要删除用户吗?">
<el-button
slot="reference"
size="mini"
@ -319,7 +319,7 @@ export default {
status: 1,
allow_lan: true,
client_dns: [{val: '114.114.114.114'}],
route_include: [],
route_include: [{val: 'all', note: '默认全局代理'}],
route_exclude: [],
link_acl: [],
},

View File

@ -0,0 +1,145 @@
<template>
<div>
<el-card>
<el-table
ref="multipleTable"
:data="tableData"
border>
<el-table-column
sortable="true"
prop="id"
label="ID"
width="60">
</el-table-column>
<el-table-column
prop="username"
label="用户名">
</el-table-column>
<el-table-column
prop="protocol"
label="协议">
</el-table-column>
<el-table-column
prop="src"
label="源IP地址">
</el-table-column>
<el-table-column
prop="dst"
label="目的IP地址">
</el-table-column>
<el-table-column
prop="dst_port"
label="目的端口">
</el-table-column>
<el-table-column
prop="created_at"
label="创建时间"
:formatter="tableDateFormat">
</el-table-column>
<el-table-column
label="操作"
width="150">
<template slot-scope="scope">
<el-popconfirm
class="m-left-10"
@confirm="handleDel(scope.row)"
title="确定要删除审计日志吗?">
<el-button
slot="reference"
size="mini"
type="danger">删除
</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="sh-20"></div>
<el-pagination
background
layout="prev, pager, next"
:pager-count="11"
@current-change="pageChange"
:total="count">
</el-pagination>
</el-card>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "Audit",
components: {},
mixins: [],
created() {
this.$emit('update:route_path', this.$route.path)
this.$emit('update:route_name', ['基础信息', 'IP审计'])
},
mounted() {
this.getData(1)
},
data() {
return {
tableData: [],
count: 10,
nowIndex: 0,
}
},
methods: {
getData(p) {
axios.get('/set/audit/list', {
params: {
page: p,
}
}).then(resp => {
var data = resp.data.data
console.log(data);
this.tableData = data.datas;
this.count = data.count
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
pageChange(p) {
this.getData(p)
},
handleDel(row) {
axios.post('/set/audit/del?id=' + row.id).then(resp => {
var rdata = resp.data
if (rdata.code === 0) {
this.$message.success(rdata.msg);
this.getData(1);
} else {
this.$message.error(rdata.msg);
}
console.log(rdata);
}).catch(error => {
this.$message.error('哦,请求出错');
console.log(error);
});
},
},
}
</script>
<style scoped>
</style>

View File

@ -76,13 +76,12 @@
<el-popconfirm
class="m-left-10"
@onConfirm="handleDel(scope.row)"
@confirm="handleDel(scope.row)"
title="确定要删除IP映射吗">
<el-button
slot="reference"
size="mini"
type="danger"
@click="handleDelete(scope.row)">删除
type="danger">删除
</el-button>
</el-popconfirm>

View File

@ -120,7 +120,7 @@
<el-popconfirm
class="m-left-10"
@onConfirm="handleDel(scope.row)"
@confirm="handleDel(scope.row)"
title="确定要删除用户吗?">
<el-button
slot="reference"

View File

@ -95,7 +95,7 @@
<el-popconfirm
class="m-left-10"
@onConfirm="handleOffline(scope.row)"
@confirm="handleOffline(scope.row)"
title="确定要下线用户吗?">
<el-button
slot="reference"

View File

@ -17,6 +17,7 @@ const routes = [
{path: 'set/system', component: () => import('@/pages/set/System')},
{path: 'set/soft', component: () => import('@/pages/set/Soft')},
{path: 'set/other', component: () => import('@/pages/set/Other')},
{path: 'set/audit', component: () => import('@/pages/set/Audit')},
{path: 'user/list', component: () => import('@/pages/user/List')},
{path: 'user/online', component: () => import('@/pages/user/Online')},