1package netlink
2
3import (
4	"fmt"
5	"net"
6	"strings"
7	"syscall"
8
9	"github.com/vishvananda/netlink/nl"
10)
11
12type PDP struct {
13	Version     uint32
14	TID         uint64
15	PeerAddress net.IP
16	MSAddress   net.IP
17	Flow        uint16
18	NetNSFD     uint32
19	ITEI        uint32
20	OTEI        uint32
21}
22
23func (pdp *PDP) String() string {
24	elems := []string{}
25	elems = append(elems, fmt.Sprintf("Version: %d", pdp.Version))
26	if pdp.Version == 0 {
27		elems = append(elems, fmt.Sprintf("TID: %d", pdp.TID))
28	} else if pdp.Version == 1 {
29		elems = append(elems, fmt.Sprintf("TEI: %d/%d", pdp.ITEI, pdp.OTEI))
30	}
31	elems = append(elems, fmt.Sprintf("MS-Address: %s", pdp.MSAddress))
32	elems = append(elems, fmt.Sprintf("Peer-Address: %s", pdp.PeerAddress))
33	return fmt.Sprintf("{%s}", strings.Join(elems, " "))
34}
35
36func (p *PDP) parseAttributes(attrs []syscall.NetlinkRouteAttr) error {
37	for _, a := range attrs {
38		switch a.Attr.Type {
39		case nl.GENL_GTP_ATTR_VERSION:
40			p.Version = native.Uint32(a.Value)
41		case nl.GENL_GTP_ATTR_TID:
42			p.TID = native.Uint64(a.Value)
43		case nl.GENL_GTP_ATTR_PEER_ADDRESS:
44			p.PeerAddress = net.IP(a.Value)
45		case nl.GENL_GTP_ATTR_MS_ADDRESS:
46			p.MSAddress = net.IP(a.Value)
47		case nl.GENL_GTP_ATTR_FLOW:
48			p.Flow = native.Uint16(a.Value)
49		case nl.GENL_GTP_ATTR_NET_NS_FD:
50			p.NetNSFD = native.Uint32(a.Value)
51		case nl.GENL_GTP_ATTR_I_TEI:
52			p.ITEI = native.Uint32(a.Value)
53		case nl.GENL_GTP_ATTR_O_TEI:
54			p.OTEI = native.Uint32(a.Value)
55		}
56	}
57	return nil
58}
59
60func parsePDP(msgs [][]byte) ([]*PDP, error) {
61	pdps := make([]*PDP, 0, len(msgs))
62	for _, m := range msgs {
63		attrs, err := nl.ParseRouteAttr(m[nl.SizeofGenlmsg:])
64		if err != nil {
65			return nil, err
66		}
67		pdp := &PDP{}
68		if err := pdp.parseAttributes(attrs); err != nil {
69			return nil, err
70		}
71		pdps = append(pdps, pdp)
72	}
73	return pdps, nil
74}
75
76func (h *Handle) GTPPDPList() ([]*PDP, error) {
77	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
78	if err != nil {
79		return nil, err
80	}
81	msg := &nl.Genlmsg{
82		Command: nl.GENL_GTP_CMD_GETPDP,
83		Version: nl.GENL_GTP_VERSION,
84	}
85	req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_DUMP)
86	req.AddData(msg)
87	msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
88	if err != nil {
89		return nil, err
90	}
91	return parsePDP(msgs)
92}
93
94func GTPPDPList() ([]*PDP, error) {
95	return pkgHandle.GTPPDPList()
96}
97
98func gtpPDPGet(req *nl.NetlinkRequest) (*PDP, error) {
99	msgs, err := req.Execute(syscall.NETLINK_GENERIC, 0)
100	if err != nil {
101		return nil, err
102	}
103	pdps, err := parsePDP(msgs)
104	if err != nil {
105		return nil, err
106	}
107	if len(pdps) != 1 {
108		return nil, fmt.Errorf("invalid reqponse for GENL_GTP_CMD_GETPDP")
109	}
110	return pdps[0], nil
111}
112
113func (h *Handle) GTPPDPByTID(link Link, tid int) (*PDP, error) {
114	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
115	if err != nil {
116		return nil, err
117	}
118	msg := &nl.Genlmsg{
119		Command: nl.GENL_GTP_CMD_GETPDP,
120		Version: nl.GENL_GTP_VERSION,
121	}
122	req := h.newNetlinkRequest(int(f.ID), 0)
123	req.AddData(msg)
124	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
125	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
126	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(uint64(tid))))
127	return gtpPDPGet(req)
128}
129
130func GTPPDPByTID(link Link, tid int) (*PDP, error) {
131	return pkgHandle.GTPPDPByTID(link, tid)
132}
133
134func (h *Handle) GTPPDPByITEI(link Link, itei int) (*PDP, error) {
135	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
136	if err != nil {
137		return nil, err
138	}
139	msg := &nl.Genlmsg{
140		Command: nl.GENL_GTP_CMD_GETPDP,
141		Version: nl.GENL_GTP_VERSION,
142	}
143	req := h.newNetlinkRequest(int(f.ID), 0)
144	req.AddData(msg)
145	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(1)))
146	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
147	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(uint32(itei))))
148	return gtpPDPGet(req)
149}
150
151func GTPPDPByITEI(link Link, itei int) (*PDP, error) {
152	return pkgHandle.GTPPDPByITEI(link, itei)
153}
154
155func (h *Handle) GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
156	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
157	if err != nil {
158		return nil, err
159	}
160	msg := &nl.Genlmsg{
161		Command: nl.GENL_GTP_CMD_GETPDP,
162		Version: nl.GENL_GTP_VERSION,
163	}
164	req := h.newNetlinkRequest(int(f.ID), 0)
165	req.AddData(msg)
166	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(0)))
167	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
168	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(addr.To4())))
169	return gtpPDPGet(req)
170}
171
172func GTPPDPByMSAddress(link Link, addr net.IP) (*PDP, error) {
173	return pkgHandle.GTPPDPByMSAddress(link, addr)
174}
175
176func (h *Handle) GTPPDPAdd(link Link, pdp *PDP) error {
177	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
178	if err != nil {
179		return err
180	}
181	msg := &nl.Genlmsg{
182		Command: nl.GENL_GTP_CMD_NEWPDP,
183		Version: nl.GENL_GTP_VERSION,
184	}
185	req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
186	req.AddData(msg)
187	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
188	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
189	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_PEER_ADDRESS, []byte(pdp.PeerAddress.To4())))
190	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_MS_ADDRESS, []byte(pdp.MSAddress.To4())))
191
192	switch pdp.Version {
193	case 0:
194		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
195		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_FLOW, nl.Uint16Attr(pdp.Flow)))
196	case 1:
197		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
198		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_O_TEI, nl.Uint32Attr(pdp.OTEI)))
199	default:
200		return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
201	}
202	_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
203	return err
204}
205
206func GTPPDPAdd(link Link, pdp *PDP) error {
207	return pkgHandle.GTPPDPAdd(link, pdp)
208}
209
210func (h *Handle) GTPPDPDel(link Link, pdp *PDP) error {
211	f, err := h.GenlFamilyGet(nl.GENL_GTP_NAME)
212	if err != nil {
213		return err
214	}
215	msg := &nl.Genlmsg{
216		Command: nl.GENL_GTP_CMD_DELPDP,
217		Version: nl.GENL_GTP_VERSION,
218	}
219	req := h.newNetlinkRequest(int(f.ID), syscall.NLM_F_EXCL|syscall.NLM_F_ACK)
220	req.AddData(msg)
221	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_VERSION, nl.Uint32Attr(pdp.Version)))
222	req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_LINK, nl.Uint32Attr(uint32(link.Attrs().Index))))
223
224	switch pdp.Version {
225	case 0:
226		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_TID, nl.Uint64Attr(pdp.TID)))
227	case 1:
228		req.AddData(nl.NewRtAttr(nl.GENL_GTP_ATTR_I_TEI, nl.Uint32Attr(pdp.ITEI)))
229	default:
230		return fmt.Errorf("unsupported GTP version: %d", pdp.Version)
231	}
232	_, err = req.Execute(syscall.NETLINK_GENERIC, 0)
233	return err
234}
235
236func GTPPDPDel(link Link, pdp *PDP) error {
237	return pkgHandle.GTPPDPDel(link, pdp)
238}
239