github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edgemesh/pkg/listener/listener.go (about)

     1  package listener
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net"
     7  	"strconv"
     8  	"strings"
     9  	"sync"
    10  	"syscall"
    11  	"unsafe"
    12  
    13  	"github.com/kubeedge/beehive/pkg/core/model"
    14  	v1 "k8s.io/api/core/v1"
    15  	"k8s.io/klog"
    16  
    17  	"github.com/kubeedge/kubeedge/common/constants"
    18  	"github.com/kubeedge/kubeedge/edge/pkg/metamanager/client"
    19  	"github.com/kubeedge/kubeedge/edgemesh/pkg/cache"
    20  	"github.com/kubeedge/kubeedge/edgemesh/pkg/config"
    21  	"github.com/kubeedge/kubeedge/edgemesh/pkg/protocol"
    22  	"github.com/kubeedge/kubeedge/edgemesh/pkg/protocol/http"
    23  )
    24  
    25  type SvcDescription struct {
    26  	sync.RWMutex
    27  	SvcPortsByIP map[string]string // key: fakeIP, value: SvcPorts
    28  	IPBySvc      map[string]string // key: svcName.svcNamespace, value: fakeIP
    29  }
    30  
    31  type sockAddr struct {
    32  	family uint16
    33  	data   [14]byte
    34  }
    35  
    36  const (
    37  	defaultNetworkPrefix = "9.251."
    38  	maxPoolSize          = 65534
    39  
    40  	SO_ORIGINAL_DST = 80
    41  )
    42  
    43  var (
    44  	svcDesc     *SvcDescription
    45  	unused      []string
    46  	indexOfPool uint16
    47  	metaClient  client.CoreInterface
    48  	once        sync.Once
    49  )
    50  
    51  func Init() {
    52  	once.Do(func() {
    53  		unused = make([]string, 0)
    54  		svcDesc = &SvcDescription{
    55  			SvcPortsByIP: make(map[string]string),
    56  			IPBySvc:      make(map[string]string),
    57  		}
    58  		// init meta client
    59  		metaClient = client.New()
    60  		// init fakeIP pool
    61  		initPool()
    62  		// recover listener meta from edge db
    63  		recoverFromDB()
    64  	})
    65  }
    66  
    67  // getSubNet converts uint16 to "uint8.uint8"
    68  func getSubNet(subNet uint16) string {
    69  	arg1 := uint64(subNet & 0x00ff)
    70  	arg2 := uint64((subNet & 0xff00) >> 8)
    71  	return strconv.FormatUint(arg2, 10) + "." + strconv.FormatUint(arg1, 10)
    72  }
    73  
    74  // initPool initializes fakeIP pool with size of 256
    75  func initPool() {
    76  	// avoid 0.0
    77  	indexOfPool = uint16(1)
    78  	for ; indexOfPool <= uint16(255); indexOfPool++ {
    79  		ip := defaultNetworkPrefix + getSubNet(indexOfPool)
    80  		unused = append(unused, ip)
    81  	}
    82  }
    83  
    84  // expandPool expands fakeIP pool, each time with size of 256
    85  func expandPool() {
    86  	end := indexOfPool + uint16(255)
    87  	for ; indexOfPool <= end; indexOfPool++ {
    88  		// avoid 255.255
    89  		if indexOfPool > maxPoolSize {
    90  			return
    91  		}
    92  		ip := defaultNetworkPrefix + getSubNet(indexOfPool)
    93  		// if ip is not used, append it to unused
    94  		if svcDesc.getSvcPorts(ip) == "" {
    95  			unused = append(unused, ip)
    96  		}
    97  	}
    98  }
    99  
   100  // reserveIp reserves used fakeIP
   101  func reserveIP(ip string) {
   102  	for i, value := range unused {
   103  		if ip == value {
   104  			unused = append(unused[:i], unused[i+1:]...)
   105  			break
   106  		}
   107  	}
   108  }
   109  
   110  // recoverFromDB gets fakeIP from edge db and assigns them to services after EdgeMesh starts
   111  func recoverFromDB() {
   112  	svcs, err := metaClient.Services("all").ListAll()
   113  	if err != nil {
   114  		klog.Errorf("[EdgeMesh] list all services from edge db error: %v", err)
   115  		return
   116  	}
   117  	for _, svc := range svcs {
   118  		svcName := svc.Namespace + "." + svc.Name
   119  		value, err := metaClient.Listener().Get(svcName)
   120  		if err != nil {
   121  			klog.Errorf("[EdgeMesh] get listener of svc %s from edge db error: %v", svcName, err)
   122  			continue
   123  		}
   124  		ip, ok := value.([]string)
   125  		if !ok {
   126  			klog.Errorf("[EdgeMesh] value %+v is not a string", value)
   127  			continue
   128  		}
   129  		if len(ip) == 0 {
   130  			svcPorts := getSvcPorts(svc, svcName)
   131  			addServer(svcName, svcPorts)
   132  			klog.Warningf("[EdgeMesh] listener %s from edge db with no ip", svcName)
   133  			continue
   134  		}
   135  		svcPorts := getSvcPorts(svc, svcName)
   136  		reserveIP(ip[0][1 : len(ip[0])-1])
   137  		svcDesc.set(svcName, ip[0][1:len(ip[0])-1], svcPorts)
   138  		klog.Infof("[EdgeMesh] get listener %s from edge db: %s", svcName, ip[0][1:len(ip[0])-1])
   139  	}
   140  }
   141  
   142  // Start starts the EdgeMesh listener
   143  func Start() {
   144  	for {
   145  		conn, err := config.Config.Listener.Accept()
   146  		if err != nil {
   147  			klog.Warningf("[EdgeMesh] get tcp conn error: %v", err)
   148  			continue
   149  		}
   150  		ip, port, err := realServerAddress(&conn)
   151  		if err != nil {
   152  			klog.Warningf("[EdgeMesh] get real destination of tcp conn error: %v", err)
   153  			conn.Close()
   154  			continue
   155  		}
   156  		proto, err := newProtocolFromSock(ip, port, conn)
   157  		if err != nil {
   158  			klog.Warningf("[EdgeMesh] get protocol from sock err: %v", err)
   159  			conn.Close()
   160  			continue
   161  		}
   162  
   163  		go proto.Process()
   164  	}
   165  }
   166  
   167  // newProtocolFromSock returns a protocol.Protocol interface if the ip is in proxy list
   168  func newProtocolFromSock(ip string, port int, conn net.Conn) (proto protocol.Protocol, err error) {
   169  	svcPorts := svcDesc.getSvcPorts(ip)
   170  	protoName, svcName := getProtocol(svcPorts, port)
   171  	if protoName == "" || svcName == "" {
   172  		return nil, fmt.Errorf("protocol name: %s or svcName: %s is invalid", protoName, svcName)
   173  	}
   174  
   175  	svcNameSets := strings.Split(svcName, ".")
   176  	if len(svcNameSets) != 2 {
   177  		return nil, fmt.Errorf("invalid length %d after splitting svc name %s", len(svcNameSets), svcName)
   178  	}
   179  	namespace := svcNameSets[0]
   180  	name := svcNameSets[1]
   181  
   182  	switch protoName {
   183  	case "http":
   184  		proto = &http.HTTP{
   185  			Conn:         conn,
   186  			SvcName:      name,
   187  			SvcNamespace: namespace,
   188  			Port:         port,
   189  		}
   190  		err = nil
   191  	default:
   192  		proto = nil
   193  		err = fmt.Errorf("protocol: %s is not supported yet", protoName)
   194  	}
   195  	return
   196  }
   197  
   198  // getProtocol gets protocol name
   199  func getProtocol(svcPorts string, port int) (string, string) {
   200  	var protoName string
   201  	sub := strings.Split(svcPorts, "|")
   202  	n := len(sub)
   203  	if n < 2 {
   204  		return "", ""
   205  	}
   206  	svcName := sub[n-1]
   207  
   208  	pstr := strconv.Itoa(port)
   209  	if pstr == "" {
   210  		return "", ""
   211  	}
   212  	for _, s := range sub {
   213  		if strings.Contains(s, pstr) {
   214  			protoName = strings.Split(s, ",")[0]
   215  			break
   216  		}
   217  	}
   218  	return protoName, svcName
   219  }
   220  
   221  // realServerAddress returns an intercepted connection's original destination.
   222  func realServerAddress(conn *net.Conn) (string, int, error) {
   223  	tcpConn, ok := (*conn).(*net.TCPConn)
   224  	if !ok {
   225  		return "", -1, fmt.Errorf("not a TCPConn")
   226  	}
   227  
   228  	file, err := tcpConn.File()
   229  	if err != nil {
   230  		return "", -1, err
   231  	}
   232  
   233  	// To avoid potential problems from making the socket non-blocking.
   234  	tcpConn.Close()
   235  	*conn, err = net.FileConn(file)
   236  	if err != nil {
   237  		return "", -1, err
   238  	}
   239  
   240  	defer file.Close()
   241  	fd := file.Fd()
   242  
   243  	var addr sockAddr
   244  	size := uint32(unsafe.Sizeof(addr))
   245  	err = getSockOpt(int(fd), syscall.SOL_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), &size)
   246  	if err != nil {
   247  		return "", -1, err
   248  	}
   249  
   250  	var ip net.IP
   251  	switch addr.family {
   252  	case syscall.AF_INET:
   253  		ip = addr.data[2:6]
   254  	default:
   255  		return "", -1, fmt.Errorf("unrecognized address family")
   256  	}
   257  
   258  	port := int(addr.data[0])<<8 + int(addr.data[1])
   259  	syscall.SetNonblock(int(fd), true)
   260  
   261  	return ip.String(), port, nil
   262  }
   263  
   264  func getSockOpt(s int, level int, name int, val uintptr, vallen *uint32) (err error) {
   265  	_, _, e1 := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0)
   266  	if e1 != 0 {
   267  		err = e1
   268  	}
   269  	return
   270  }
   271  
   272  // filterResourceTypeService implements msg filter for "Service" and "ServiceList" resource
   273  func filterResourceTypeService(msg model.Message) []v1.Service {
   274  	svcs := make([]v1.Service, 0)
   275  	content, err := json.Marshal(msg.GetContent())
   276  	if err != nil || len(content) == 0 {
   277  		return svcs
   278  	}
   279  	switch getResourceType(msg.GetResource()) {
   280  	case constants.ResourceTypeService:
   281  		s, err := handleServiceMessage(content)
   282  		if err != nil {
   283  			break
   284  		}
   285  		svcs = append(svcs, *s)
   286  	case constants.ResourceTypeServiceList:
   287  		ss, err := handleServiceListMessage(content)
   288  		if err != nil {
   289  			break
   290  		}
   291  		svcs = append(svcs, ss...)
   292  	default:
   293  		break
   294  	}
   295  
   296  	return svcs
   297  }
   298  
   299  func getSvcPorts(svc v1.Service, svcName string) string {
   300  	svcPorts := ""
   301  	for _, p := range svc.Spec.Ports {
   302  		pro := strings.Split(p.Name, "-")
   303  		sub := fmt.Sprintf("%s,%d,%d|", pro[0], p.Port, p.TargetPort.IntVal)
   304  		svcPorts = svcPorts + sub
   305  	}
   306  	svcPorts += svcName
   307  	return svcPorts
   308  }
   309  
   310  // MsgProcess processes messages from metaManager
   311  func MsgProcess(msg model.Message) {
   312  	// process services
   313  	if svcs := filterResourceTypeService(msg); len(svcs) != 0 {
   314  		klog.Infof("[EdgeMesh] %s services: %d resource: %s", msg.GetOperation(), len(svcs), msg.Router.Resource)
   315  		for _, svc := range svcs {
   316  			svcName := svc.Namespace + "." + svc.Name
   317  			svcPorts := getSvcPorts(svc, svcName)
   318  			switch msg.GetOperation() {
   319  			case "insert":
   320  				cache.GetMeshCache().Add("service"+"."+svcName, &svc)
   321  				klog.Infof("[EdgeMesh] insert svc %s.%s into cache", svc.Namespace, svc.Name)
   322  				addServer(svcName, svcPorts)
   323  			case "update":
   324  				cache.GetMeshCache().Add("service"+"."+svcName, &svc)
   325  				klog.Infof("[EdgeMesh] update svc %s.%s in cache", svc.Namespace, svc.Name)
   326  				updateServer(svcName, svcPorts)
   327  			case "delete":
   328  				cache.GetMeshCache().Remove("service" + "." + svcName)
   329  				klog.Infof("[EdgeMesh] delete svc %s.%s from cache", svc.Namespace, svc.Name)
   330  				delServer(svcName)
   331  			default:
   332  				klog.Warningf("[EdgeMesh] invalid %s operation on services", msg.GetOperation())
   333  			}
   334  		}
   335  		return
   336  	}
   337  	// process pods
   338  	if getResourceType(msg.GetResource()) == model.ResourceTypePodlist {
   339  		klog.Infof("[EdgeMesh] %s podlist, resource: %s", msg.GetOperation(), msg.Router.Resource)
   340  		pods := make([]v1.Pod, 0)
   341  		content, err := json.Marshal(msg.GetContent())
   342  		if err != nil {
   343  			klog.Errorf("[EdgeMesh] marshal podlist msg content err: %v", err)
   344  			return
   345  		}
   346  		pods, err = handlePodListMessage(content)
   347  		if err != nil {
   348  			return
   349  		}
   350  		podListName := getResourceName(msg.GetResource())
   351  		podListNamespace := getResourceNamespace(msg.GetResource())
   352  		switch msg.GetOperation() {
   353  		case "insert", "update":
   354  			cache.GetMeshCache().Add("pods"+"."+podListNamespace+"."+podListName, pods)
   355  			klog.Infof("[EdgeMesh] insert/update pods %s.%s into cache", podListNamespace, podListName)
   356  		case "delete":
   357  			cache.GetMeshCache().Remove("pods" + "." + podListNamespace + "." + podListName)
   358  			klog.Infof("[EdgeMesh] delete pods %s.%s from cache", podListNamespace, podListName)
   359  		default:
   360  			klog.Warningf("[EdgeMesh] invalid %s operation on podlist", msg.GetOperation())
   361  		}
   362  	}
   363  	return
   364  }
   365  
   366  // addServer adds a server
   367  func addServer(svcName, svcPorts string) {
   368  	ip := svcDesc.getIP(svcName)
   369  	if ip != "" {
   370  		svcDesc.set(svcName, ip, svcPorts)
   371  		return
   372  	} else {
   373  		if len(unused) == 0 {
   374  			// try to expand
   375  			expandPool()
   376  			if len(unused) == 0 {
   377  				klog.Warningf("[EdgeMesh] insufficient fake IP !!")
   378  				return
   379  			}
   380  		}
   381  		ip = unused[0]
   382  		unused = unused[1:]
   383  	}
   384  	svcDesc.set(svcName, ip, svcPorts)
   385  	err := metaClient.Listener().Add(svcName, ip)
   386  	if err != nil {
   387  		klog.Errorf("[EdgeMesh] add listener %s to edge db error: %v", svcName, err)
   388  		return
   389  	}
   390  	return
   391  }
   392  
   393  // updateServer updates a server
   394  func updateServer(svcName, svcPorts string) {
   395  	ip := svcDesc.getIP(svcName)
   396  	if ip == "" {
   397  		if len(unused) == 0 {
   398  			// try to expand
   399  			expandPool()
   400  			if len(unused) == 0 {
   401  				klog.Warningf("[EdgeMesh] insufficient fake IP !!")
   402  				return
   403  			}
   404  		}
   405  		ip = unused[0]
   406  		unused = unused[1:]
   407  		err := metaClient.Listener().Add(svcName, ip)
   408  		if err != nil {
   409  			klog.Errorf("[EdgeMesh] add listener %s to edge db error: %v", svcName, err)
   410  		}
   411  	}
   412  	svcDesc.set(svcName, ip, svcPorts)
   413  }
   414  
   415  // delServer deletes a server
   416  func delServer(svcName string) {
   417  	ip := svcDesc.getIP(svcName)
   418  	if ip == "" {
   419  		return
   420  	}
   421  	svcDesc.del(svcName, ip)
   422  	err := metaClient.Listener().Del(svcName)
   423  	if err != nil {
   424  		klog.Errorf("[EdgeMesh] delete listener from edge db error: %v", err)
   425  	}
   426  	// recycling fakeIP
   427  	unused = append(unused, ip)
   428  }
   429  
   430  // handleServiceMessage converts bytes to k8s service meta
   431  func handleServiceMessage(content []byte) (*v1.Service, error) {
   432  	var s v1.Service
   433  	err := json.Unmarshal(content, &s)
   434  	if err != nil {
   435  		klog.Errorf("[EdgeMesh] unmarshal message to k8s service failed, err: %v", err)
   436  		return nil, err
   437  	}
   438  	return &s, nil
   439  }
   440  
   441  // handleServiceListMessage converts bytes to k8s service list meta
   442  func handleServiceListMessage(content []byte) ([]v1.Service, error) {
   443  	var ss []v1.Service
   444  	err := json.Unmarshal(content, &ss)
   445  	if err != nil {
   446  		klog.Errorf("[EdgeMesh] unmarshal message to k8s service list failed, err: %v", err)
   447  		return nil, err
   448  	}
   449  	return ss, nil
   450  }
   451  
   452  // handlePodListMessage converts bytes to k8s pod list meta
   453  func handlePodListMessage(content []byte) ([]v1.Pod, error) {
   454  	var pp []v1.Pod
   455  	err := json.Unmarshal(content, &pp)
   456  	if err != nil {
   457  		klog.Errorf("[EdgeMesh] unmarshal message to k8s pod list failed, err: %v", err)
   458  		return nil, err
   459  	}
   460  	return pp, nil
   461  }
   462  
   463  // getResourceType returns the resource type as a string
   464  func getResourceType(resource string) string {
   465  	str := strings.Split(resource, "/")
   466  	if len(str) == 3 {
   467  		return str[1]
   468  	} else if len(str) == 5 {
   469  		return str[3]
   470  	} else {
   471  		return resource
   472  	}
   473  }
   474  
   475  // getResourceName returns the resource name as a string
   476  func getResourceName(resource string) string {
   477  	str := strings.Split(resource, "/")
   478  	if len(str) == 3 {
   479  		return str[2]
   480  	} else if len(str) == 5 {
   481  		return str[4]
   482  	} else {
   483  		return resource
   484  	}
   485  }
   486  
   487  // getResourceNamespace returns the resource namespace as a string
   488  func getResourceNamespace(resource string) string {
   489  	str := strings.Split(resource, "/")
   490  	if len(str) == 3 {
   491  		return str[0]
   492  	} else if len(str) == 5 {
   493  		return str[2]
   494  	} else {
   495  		return resource
   496  	}
   497  }
   498  
   499  // set is a thread-safe operation to add to map
   500  func (sd *SvcDescription) set(svcName, ip, svcPorts string) {
   501  	sd.Lock()
   502  	defer sd.Unlock()
   503  	sd.IPBySvc[svcName] = ip
   504  	sd.SvcPortsByIP[ip] = svcPorts
   505  }
   506  
   507  // del is a thread-safe operation to del from map
   508  func (sd *SvcDescription) del(svcName, ip string) {
   509  	sd.Lock()
   510  	defer sd.Unlock()
   511  	delete(sd.IPBySvc, svcName)
   512  	delete(sd.SvcPortsByIP, ip)
   513  }
   514  
   515  // getIP is a thread-safe operation to get from map
   516  func (sd *SvcDescription) getIP(svcName string) string {
   517  	sd.RLock()
   518  	defer sd.RUnlock()
   519  	ip := sd.IPBySvc[svcName]
   520  	return ip
   521  }
   522  
   523  // getSvcPorts is a thread-safe operation to get from map
   524  func (sd *SvcDescription) getSvcPorts(ip string) string {
   525  	sd.RLock()
   526  	defer sd.RUnlock()
   527  	svcPorts := sd.SvcPortsByIP[ip]
   528  	return svcPorts
   529  }
   530  
   531  // GetServiceServer returns the proxy IP by given service name
   532  func GetServiceServer(svcName string) string {
   533  	ip := svcDesc.getIP(svcName)
   534  	return ip
   535  }