github.com/TeaOSLab/EdgeNode@v1.3.8/internal/iplibrary/manager_ip_list.go (about)

     1  package iplibrary
     2  
     3  import (
     4  	"github.com/TeaOSLab/EdgeCommon/pkg/iputils"
     5  	"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
     6  	"github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb"
     7  	teaconst "github.com/TeaOSLab/EdgeNode/internal/const"
     8  	"github.com/TeaOSLab/EdgeNode/internal/events"
     9  	"github.com/TeaOSLab/EdgeNode/internal/goman"
    10  	"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
    11  	"github.com/TeaOSLab/EdgeNode/internal/rpc"
    12  	"github.com/TeaOSLab/EdgeNode/internal/trackers"
    13  	"github.com/TeaOSLab/EdgeNode/internal/utils/idles"
    14  	"github.com/TeaOSLab/EdgeNode/internal/waf"
    15  	"github.com/TeaOSLab/EdgeNode/internal/zero"
    16  	"github.com/iwind/TeaGo/Tea"
    17  	"github.com/iwind/TeaGo/types"
    18  	"os"
    19  	"sync"
    20  	"time"
    21  )
    22  
    23  var SharedIPListManager = NewIPListManager()
    24  var IPListUpdateNotify = make(chan bool, 1)
    25  
    26  func init() {
    27  	if !teaconst.IsMain {
    28  		return
    29  	}
    30  
    31  	events.On(events.EventLoaded, func() {
    32  		goman.New(func() {
    33  			SharedIPListManager.Start()
    34  		})
    35  	})
    36  	events.OnClose(func() {
    37  		SharedIPListManager.Stop()
    38  	})
    39  
    40  	var ticker = time.NewTicker(24 * time.Hour)
    41  	goman.New(func() {
    42  		idles.RunTicker(ticker, func() {
    43  			SharedIPListManager.DeleteExpiredItems()
    44  		})
    45  	})
    46  }
    47  
    48  // IPListManager IP名单管理
    49  type IPListManager struct {
    50  	ticker *time.Ticker
    51  
    52  	db IPListDB
    53  
    54  	lastVersion   int64
    55  	fetchPageSize int64
    56  
    57  	listMap map[int64]*IPList
    58  	mu      sync.RWMutex
    59  
    60  	isFirstTime bool
    61  }
    62  
    63  func NewIPListManager() *IPListManager {
    64  	return &IPListManager{
    65  		fetchPageSize: 5_000,
    66  		listMap:       map[int64]*IPList{},
    67  		isFirstTime:   true,
    68  	}
    69  }
    70  
    71  func (this *IPListManager) Start() {
    72  	this.Init()
    73  
    74  	// 第一次读取
    75  	err := this.Loop()
    76  	if err != nil {
    77  		remotelogs.ErrorObject("IP_LIST_MANAGER", err)
    78  	}
    79  
    80  	this.ticker = time.NewTicker(60 * time.Second)
    81  	if Tea.IsTesting() {
    82  		this.ticker = time.NewTicker(10 * time.Second)
    83  	}
    84  	var countErrors = 0
    85  	for {
    86  		select {
    87  		case <-this.ticker.C:
    88  		case <-IPListUpdateNotify:
    89  		}
    90  		err = this.Loop()
    91  		if err != nil {
    92  			countErrors++
    93  
    94  			remotelogs.ErrorObject("IP_LIST_MANAGER", err)
    95  
    96  			// 连续错误小于3次的我们立即重试
    97  			if countErrors <= 3 {
    98  				select {
    99  				case IPListUpdateNotify <- true:
   100  				default:
   101  				}
   102  			}
   103  		} else {
   104  			countErrors = 0
   105  		}
   106  	}
   107  }
   108  
   109  func (this *IPListManager) Stop() {
   110  	if this.ticker != nil {
   111  		this.ticker.Stop()
   112  	}
   113  }
   114  
   115  func (this *IPListManager) Init() {
   116  	// 从数据库中当中读取数据
   117  	// 检查sqlite文件是否存在,以便决定使用sqlite还是kv
   118  	var sqlitePath = Tea.Root + "/data/ip_list.db"
   119  	_, sqliteErr := os.Stat(sqlitePath)
   120  
   121  	var db IPListDB
   122  	var err error
   123  	if sqliteErr == nil || !teaconst.EnableKVCacheStore {
   124  		db, err = NewSQLiteIPList()
   125  	} else {
   126  		db, err = NewKVIPList()
   127  	}
   128  
   129  	if err != nil {
   130  		remotelogs.Error("IP_LIST_MANAGER", "create ip list local database failed: "+err.Error())
   131  	} else {
   132  		this.db = db
   133  
   134  		// 删除本地数据库中过期的条目
   135  		_ = db.DeleteExpiredItems()
   136  
   137  		// 本地数据库中最大版本号
   138  		this.lastVersion, err = db.ReadMaxVersion()
   139  		if err != nil {
   140  			remotelogs.Error("IP_LIST_MANAGER", "find max version failed: "+err.Error())
   141  			this.lastVersion = 0
   142  		}
   143  		remotelogs.Println("IP_LIST_MANAGER", "starting from '"+db.Name()+"' version '"+types.String(this.lastVersion)+"' ...")
   144  
   145  		// 从本地数据库中加载
   146  		var offset int64 = 0
   147  		var size int64 = 2_000
   148  
   149  		var tr = trackers.Begin("IP_LIST_MANAGER:load")
   150  		defer tr.End()
   151  
   152  		for {
   153  			items, goNext, readErr := db.ReadItems(offset, size)
   154  			var l = len(items)
   155  			if readErr != nil {
   156  				remotelogs.Error("IP_LIST_MANAGER", "read ip list from local database failed: "+readErr.Error())
   157  			} else {
   158  				this.processItems(items, false)
   159  				if !goNext {
   160  					break
   161  				}
   162  			}
   163  			offset += int64(l)
   164  		}
   165  	}
   166  }
   167  
   168  func (this *IPListManager) Loop() error {
   169  	// 是否同步IP名单
   170  	nodeConfig, _ := nodeconfigs.SharedNodeConfig()
   171  	if nodeConfig != nil && !nodeConfig.EnableIPLists {
   172  		return nil
   173  	}
   174  
   175  	// 第一次同步则打印信息
   176  	if this.isFirstTime {
   177  		remotelogs.Println("IP_LIST_MANAGER", "initializing ip items ...")
   178  	}
   179  
   180  	for {
   181  		hasNext, err := this.fetch()
   182  		if err != nil {
   183  			return err
   184  		}
   185  		if !hasNext {
   186  			break
   187  		}
   188  		time.Sleep(1 * time.Second)
   189  	}
   190  
   191  	// 第一次同步则打印信息
   192  	if this.isFirstTime {
   193  		this.isFirstTime = false
   194  		remotelogs.Println("IP_LIST_MANAGER", "finished initializing ip items")
   195  	}
   196  
   197  	return nil
   198  }
   199  
   200  func (this *IPListManager) fetch() (hasNext bool, err error) {
   201  	rpcClient, err := rpc.SharedRPC()
   202  	if err != nil {
   203  		return false, err
   204  	}
   205  	itemsResp, err := rpcClient.IPItemRPC.ListIPItemsAfterVersion(rpcClient.Context(), &pb.ListIPItemsAfterVersionRequest{
   206  		Version: this.lastVersion,
   207  		Size:    this.fetchPageSize,
   208  	})
   209  	if err != nil {
   210  		if rpc.IsConnError(err) {
   211  			remotelogs.Debug("IP_LIST_MANAGER", "rpc connection error: "+err.Error())
   212  			return false, nil
   213  		}
   214  		return false, err
   215  	}
   216  	var items = itemsResp.IpItems
   217  	if len(items) == 0 {
   218  		return false, nil
   219  	}
   220  
   221  	// 保存到本地数据库
   222  	if this.db != nil {
   223  		for _, item := range items {
   224  			err = this.db.AddItem(item)
   225  			if err != nil {
   226  				remotelogs.Error("IP_LIST_MANAGER", "insert item to local database failed: "+err.Error())
   227  			}
   228  		}
   229  	}
   230  
   231  	this.processItems(items, true)
   232  
   233  	return true, nil
   234  }
   235  
   236  func (this *IPListManager) FindList(listId int64) *IPList {
   237  	this.mu.RLock()
   238  	var list = this.listMap[listId]
   239  	this.mu.RUnlock()
   240  
   241  	return list
   242  }
   243  
   244  func (this *IPListManager) DeleteExpiredItems() {
   245  	if this.db != nil {
   246  		_ = this.db.DeleteExpiredItems()
   247  	}
   248  }
   249  
   250  func (this *IPListManager) ListMap() map[int64]*IPList {
   251  	return this.listMap
   252  }
   253  
   254  // 处理IP条目
   255  func (this *IPListManager) processItems(items []*pb.IPItem, fromRemote bool) {
   256  	var changedLists = map[*IPList]zero.Zero{}
   257  	for _, item := range items {
   258  		// 调试
   259  		if Tea.IsTesting() {
   260  			this.debugItem(item)
   261  		}
   262  
   263  		var list *IPList
   264  		// TODO 实现节点专有List
   265  		if item.ServerId > 0 { // 服务专有List
   266  			switch item.ListType {
   267  			case "black":
   268  				list = SharedServerListManager.FindBlackList(item.ServerId, true)
   269  			case "white":
   270  				list = SharedServerListManager.FindWhiteList(item.ServerId, true)
   271  			}
   272  		} else if item.IsGlobal { // 全局List
   273  			switch item.ListType {
   274  			case "black":
   275  				list = GlobalBlackIPList
   276  			case "white":
   277  				list = GlobalWhiteIPList
   278  			}
   279  		} else { // 其他List
   280  			this.mu.Lock()
   281  			list = this.listMap[item.ListId]
   282  			this.mu.Unlock()
   283  		}
   284  		if list == nil {
   285  			list = NewIPList()
   286  			this.mu.Lock()
   287  			this.listMap[item.ListId] = list
   288  			this.mu.Unlock()
   289  		}
   290  
   291  		changedLists[list] = zero.New()
   292  
   293  		if item.IsDeleted {
   294  			list.Delete(uint64(item.Id))
   295  
   296  			// 从WAF名单中删除
   297  			waf.SharedIPBlackList.RemoveIP(item.IpFrom, item.ServerId, fromRemote)
   298  
   299  			// 操作事件
   300  			if fromRemote {
   301  				SharedActionManager.DeleteItem(item.ListType, item)
   302  			}
   303  
   304  			continue
   305  		}
   306  
   307  		list.AddDelay(&IPItem{
   308  			Id:         uint64(item.Id),
   309  			Type:       item.Type,
   310  			IPFrom:     iputils.ToBytes(item.IpFrom),
   311  			IPTo:       iputils.ToBytes(item.IpTo),
   312  			ExpiredAt:  item.ExpiredAt,
   313  			EventLevel: item.EventLevel,
   314  		})
   315  
   316  		// 事件操作
   317  		if fromRemote {
   318  			SharedActionManager.DeleteItem(item.ListType, item)
   319  			SharedActionManager.AddItem(item.ListType, item)
   320  		}
   321  	}
   322  
   323  	if len(changedLists) > 0 {
   324  		for changedList := range changedLists {
   325  			changedList.Sort()
   326  		}
   327  	}
   328  
   329  	if fromRemote {
   330  		var latestVersion = items[len(items)-1].Version
   331  		if latestVersion > this.lastVersion {
   332  			this.lastVersion = latestVersion
   333  		}
   334  	}
   335  }
   336  
   337  // 调试IP信息
   338  func (this *IPListManager) debugItem(item *pb.IPItem) {
   339  	var ipRange = item.IpFrom
   340  	if len(item.IpTo) > 0 {
   341  		ipRange += " - " + item.IpTo
   342  	}
   343  
   344  	if item.IsDeleted {
   345  		remotelogs.Debug("IP_ITEM_DEBUG", "delete '"+ipRange+"'")
   346  	} else {
   347  		remotelogs.Debug("IP_ITEM_DEBUG", "add '"+ipRange+"'")
   348  	}
   349  }