sargon / ddhcpd Goto Github PK
View Code? Open in Web Editor NEWA distributed DHCP Daemon
License: GNU General Public License v3.0
A distributed DHCP Daemon
License: GNU General Public License v3.0
New users (including me) are likely confused by using the raw DHCP codes so the service file could be extended honoring dhcp
and network
settings.
Or any reasons against that?
We could replace ddhcpctl with uc from https://github.com/christf/uc.git saving some space in the image and further modularizing our builds.
uc is more generic and also used by prefixd and can be used for diagnostics together with l3roamd.
echo get_blocks | uc /tmp/ddhcpd_ctl
ddhcpd requires a different input format at the moment, this change would have implications on the design of the socket. In the code is a TODO for it to be redesigned. This could be an option.
There is a bug in the shifting logic in dhcp_options.
When not specifying -o 28:4:a.b.c.d an invalid broadcast address is distributed.
For -N 10.126.0.0/16 the broadcast address 0.126.255.255 is served.
https://github.com/sargon/ddhcpd/blob/master/main.c#L130 allocates the config on the heap. As far as I can see the stack frame of the main method should be valid throughout the whole lifetime of the program. I think we could just put it on the stack, couldn't we? That way we won't have to deal with allocation failues.
The same goes for the event buffer at https://github.com/sargon/ddhcpd/blob/master/main.c#L363
also for the packet buffer at https://github.com/sargon/ddhcpd/blob/master/main.c#L340
and even the packet buffer in https://github.com/sargon/ddhcpd/blob/master/ddhcpctl.c#L28
In l3 networks the mesh interface for ddhcpd is the mmfd interface. Over the run-time of the node, this interface might be destroyed and re-created. At that time ddhcpd does not update the scope-id and attempts to re-use the old scope-id from that interface that already has been destroyed.
This results in block claims not happening any more and in
Mon Jan 6 09:22:52 2020 daemon.err ddhcpd[3128]: ERROR: send_packet_mcast(...): Failed (13): Permission denied
at the time when this log message was captured, there was no interface with id 13. The proper interface would have been
23: mmfd0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN group default qlen 500
Cf. list of DHCP-related RFCs at http://www.bind9.net/rfc-dhcp
We should have a look which of those we want to implement and schedule implementation accordingly by opening some further issues.
since lede all packages must reside inside an extra folder:
mkdir ddhcpd
mv * ddhcpd/
Implement decoding of options for RFC 7710 (which basically translates to implementing decoders for
RFC7228 Section 5).
setting loglevel via compile-option is inconvenient as it forces the decision on the person building the firmware whether everyone using the resulting images will be a developer. At the same time it is hampering analysis in images that were not built with verbose debug log in case a ddhcpd issue arises.
Being able to adjust the loglevel would help.
the client in my case ends up with many ipv4 addresses in the same subnet.
Two of the currently three assigned addresses are within the same block, the third is in another block.
root@60489-luna:/tmp# ddhcpdctl -b
block size/number 4/16384
tentative timeout 15
timeout 60
refresh factor 4
spare 1
network: 10.126.0.0/16
node id 0000000000000000
ddhcp blocks
index state owner claim leases timeout
4275 4 0000000000000000 3 0/4 48
4539 2 0000000000000000 0 - 12
5403 4 0000000000000000 3 0/2 48
7024 2 0000000000000000 0 - 9
14891 4 0000000000000000 3 0/4 48
it is replaced by the hook because of the usage of execl()
We could fork a process early and then pass events by a unnamed socket.
This would solve the issue with multiple hook script instances, forking
on every event and prevent and resources over consumption by an event
spike.
If you use gluon-reconfigure
you get this error:
/usr/bin/gluon-reconfigure: line 9: ./500-ddhcpd: Permission denied
Maybe we have to edit https://github.com/sargon/gluon-sargon/tree/master/ddhcpd ?
ddhcpd is started with
/usr/sbin/ddhcpd -D -s 1 -b 2 -c local-node -i mmfd0 -N 10.139.0.0 17 -o 51 4 0 0 1 44 -o 3 4 10 139 0 1 -o 6 4 10 139 0 1
but default gateway are not assigned to clients. According to tcpdump they are not provided by ddhcpd. Am I missing something?
As some features in the pipeline may not be of much use for minimalistic clients it would be advisable to have a central point to configure such features (e.g. having a features.h, that is possibly auto-generated in the Makefile if it doesn't exist). The format could be as simple as a list of defines which only carry boolean values.
Cf. https://tools.ietf.org/html/rfc4388
Note update by https://tools.ietf.org/html/rfc6148
I have run DDHCPD on Raspberry Pi successfuly. However, when I have specified verbosity twice: sudo ./ddhcpd -c eth0 -i eth0 -v -v
, I got segmentation error after about 10-15 seconds. The output looks like this:
ubuntu@ubuntu:~/ddhcpd$ sudo ./ddhcpd -c eth0 -i eth0 -v -v
INFO: CONFIG: network=10.0.0.0/24
INFO: CONFIG: block_size=32
INFO: CONFIG: #blocks=8
INFO: CONFIG: #spare_leases=2
INFO: CONFIG: timeout=60
INFO: CONFIG: refresh_factor=4
INFO: CONFIG: tentative_timeout=15
INFO: CONFIG: client_interface=eth0
INFO: CONFIG: group_interface=eth0
DEBUG: ddhcp_block_init(config)
DEBUG: find_in_option_store(store, code:1)
DEBUG: set_in_option_store(store, code:1, len4)
DEBUG: find_in_option_store(store, code:1)
DEBUG: set_in_option_store(...): inserting option
DEBUG: find_in_option_store(store, code:2)
DEBUG: set_in_option_store(store, code:2, len4)
DEBUG: find_in_option_store(store, code:2)
DEBUG: set_in_option_store(...): inserting option
DEBUG: find_in_option_store(store, code:28)
DEBUG: set_in_option_store(store, code:28, len4)
DEBUG: find_in_option_store(store, code:28)
DEBUG: set_in_option_store(...): inserting option
DEBUG: find_in_option_store(store, code:51)
DEBUG: set_in_option_store(store, code:51, len4)
DEBUG: find_in_option_store(store, code:51)
DEBUG: set_in_option_store(...): inserting option
DEBUG: find_in_option_store(store, code:54)
DEBUG: set_in_option_store(store, code:54, len4)
DEBUG: find_in_option_store(store, code:54)
DEBUG: set_in_option_store(...): inserting option
DEBUG: epoll_add_fd(3,-2147483647)
DEBUG: epoll_add_fd(3,-2147483647)
DEBUG: epoll_add_fd(3,-2147483647)
DEBUG: epoll_add_fd(3,-2147483647)
DEBUG: netlink_init(config)
DEBUG: epoll_add_fd(3,-2147483647)
INFO: loop timeout: 7500 msecs
DEBUG: netlink_callback(...): callback triggered
DEBUG: netlink_callback(...): iface(3) up
DEBUG: ntoh_dhcp_packet(...): package len:296
DEBUG: BOOTP [ op 1, htype 1, hlen 6, hops 0, xid 1106945831, secs 128, flags 0, ciaddr 0.0.0.0, DEBUG: yiaddr 0.0.0.0, DEBUG: siaddr 0.0.0.0, DEBUG: giaddr 0.0.0.0, sname: , file: ]
DEBUG: DHCP OPTION [ code 53, length 1, value 1 ]
DEBUG: DHCP OPTION [ code 61, length 19 ]
DEBUG: DHCP OPTION [ code 55, length 11, value 1 3 12 15 6 26 33 121 119 42 120 ]
DEBUG: DHCP OPTION [ code 57, length 2 ]
DEBUG: DHCP OPTION [ code 50, length 4 ]
DEBUG: DHCP OPTION [ code 12, length 6 ]
DEBUG: DHCP OPTION [ code 255, length 0 ]
DEBUG: dhcp_hdl_discover(socket:8, packet, config)
DEBUG: block_find_free_leases(config)
DEBUG: block_find_free_leases(...): No block found!
DEBUG: dhcp_hdl_discover(...): no block with free leases found
DEBUG: Segmentation fault
If I set up verbosity with only one flag, everything is working perfectly. Raspberry is running on ubuntu server 20.04 for raspberries.
Line 207 in 9fd32f2
echo '#define REVISION "3cbb8be3435996fdb33ad7b63e3ad10481e5603a"' > version.h
gcc -Wall -Wextra -pedantic -Werror -flto -fno-strict-aliasing -std=gnu11 -D_GNU_SOURCE -MD -MP -DNDEBUG -c -o dhcp_packet.o dhcp_packet.c
dhcp_packet.c: In function ‘ntoh_dhcp_packet’:
dhcp_packet.c:207:12: error: this statement may fall through [-Werror=implicit-fallthrough=]
exit = 1;
~~~~~^~~
dhcp_packet.c:209:5: note: here
case DHCP_CODE_PAD:
^~~~
cc1: all warnings being treated as errors
make: *** [<builtin>: dhcp_packet.o] Error 1
We should check if we are fully compliant to RFC 2132 and if all necessary features mentioned in there are properly implemented.
As you offer a CLI changing parameters on the run, it should be easy to add some UBUS functionality.
Is that a planned feature?
I built the current ffki-site nightly version, but the build with -j1 V=s
stops with this error
make[4]: Entering directory '/media/ruben/linuxbackup/gluon/packages/sargon/ddhcpd'
CFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -DLOG_LEVEL=20 -mips16 -minterlink-mips16 -iremap/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28:ddhcpd-2018-06-28 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -I/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/usr/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/usr/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include/fortify -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include " CXXFLAGS="-Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -DLOG_LEVEL=20 -mips16 -minterlink-mips16 -iremap/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28:ddhcpd-2018-06-28 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro -I/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/usr/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/usr/include -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include/fortify -I/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/include " LDFLAGS="-L/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/usr/lib -L/media/ruben/linuxbackup/gluon/lede/staging_dir/target-mips_24kc_musl-1.1.16/lib -L/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/usr/lib -L/media/ruben/linuxbackup/gluon/lede/staging_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/lib -znow -zrelro " make -C /media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/. AR="mips-openwrt-linux-musl-gcc-ar" AS="mips-openwrt-linux-musl-gcc -c -Os -pipe -mno-branch-likely -mips32r2 -mtune=24kc -fno-caller-saves -fno-plt -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -msoft-float -DLOG_LEVEL=20 -iremap/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28:ddhcpd-2018-06-28 -Wformat -Werror=format-security -fstack-protector -D_FORTIFY_SOURCE=1 -Wl,-z,now -Wl,-z,relro" LD=mips-openwrt-linux-musl-ld NM="mips-openwrt-linux-musl-gcc-nm" CC="mips-openwrt-linux-musl-gcc" GCC="mips-openwrt-linux-musl-gcc" CXX="mips-openwrt-linux-musl-g++" RANLIB="mips-openwrt-linux-musl-gcc-ranlib" STRIP=mips-openwrt-linux-musl-strip OBJCOPY=mips-openwrt-linux-musl-objcopy OBJDUMP=mips-openwrt-linux-musl-objdump SIZE=mips-openwrt-linux-musl-size CROSS="mips-openwrt-linux-musl-" ARCH="mips" DESTDIR="/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/ipkg-install" install;
make[5]: Entering directory '/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28'
install -D -p -m 755 ddhcpd /media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/ipkg-install/usr/sbin/ddhcpd
install: cannot stat 'ddhcpd': No such file or directory
Makefile:59: recipe for target 'install' failed
make[5]: *** [install] Error 1
make[5]: Leaving directory '/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28'
Makefile:36: recipe for target '/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/.built' failed
make[4]: *** [/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/.built] Error 2
make[4]: Leaving directory '/media/ruben/linuxbackup/gluon/packages/sargon/ddhcpd'
package/Makefile:105: recipe for target 'package/feeds/packages_sargon/ddhcpd/compile' failed
indeed the folder
/media/ruben/linuxbackup/gluon/lede/build_dir/target-mips_24kc_musl-1.1.16/ddhcpd-2018-06-28/ipkg-install/usr/sbin/
is empty
I found a ddhcpd package but it's specialized for Gluon. As other community mesh projects or use cases could profit of this work, can we create a generic package version to merge in upstream?
The Gluon specific package could simply add ddhcpd
as a dependence.
LibreMesh uses dnsmasq-dhcpv6
and lease sharing via alfred
to allow IPv6 roaming. Would it be possible to integrate IPV6 support here as well so it'd be a full DHCP replacement?
when used together with uradvd on server and networkmanager on the client side, the small timeouts lead to the resolv.conf being overwritten frequently and ipv6 nameserver being blocked.
This obviously is a client-side issue. Maybe add/link some docs how to overcome this and both nameservers (ipv6 and ipv4) remain active?
As of RFC2131 we should support DHCPINFORM message type, to be honest I don't expect many clients to be able to use this feature, but we should support it.
In a l2 mesh we can globally set the netmask and broadcast addresses. ddhcp currently allows that relying on the appropriate dhcp options as specified by -o.
In l3 networks the netmask and broadcast address should be different for every node in the net.
To make matters worse, these values change when new blocks are reserved by ddhcpd for this given node.
Without subnet mask, dhclient (default on many linux distributions) will not accept default gateway and ipv4 will be broken.
I am not sure how best deal with that. I see two options:
After discussion with @sargon it was agreed to friendly-fork this repo as part of the gluon project so that maintainership can be shared.
the repository is available here: https://github.com/freifunk-gluon/ddhcpd
This project is GPL 3.0, but netsock is GPL 2.0-only.
It seems like, we are leasing from newer blocks instead of reusing already used blocks.
block size/number 4/256
node id 00163EA4A1210000
ddhcp blocks
index state owner claim leases timeout
144 4 00163EA4A1210000 3 0/1 15
225 4 00163EA4A1210000 3 0/1 15
In the leases field the first value are "reserved" addresses by a in progress dhcp discovery event and
the second value are the leased addresses.
We should record some network statistics to improve the number of messages needed.
Numbers I would be interested are:
Furthermore we could extend the ctl binary to get and reset those counters, so we are able to
transfer them to some statistics service.
currently the hooks only offer address and mac, would it be possible to add the clients hostname as an arg as well?
on gluon I was able to see:
[41544.407792]
[41544.407792] do_page_fault(): sending SIGSEGV to ddhcpd for invalid write access to 00d5524c
[41544.414761] epc = 77cdaf5c in libc.so[77cb2000+92000]
[41544.419729] ra = 77cdaf34 in libc.so[77cb2000+92000]
[41544.424756]
attached is the core file (rename from png)
unfortunately I cannot see much with gdb:
christof@build:/var/lib/jenkins/workspace/babel-dev/wspace/lede$ ./build_dir/toolchain-mips_24kc_gcc-5.4.0_musl-1.1.16/gdb-8.0.1/gdb/gdb ./staging_dir/target-mips_24kc_musl-1.1.16/root-ar71xx/usr/sbin/ddhcpd /tmp/ddhcpd.1529728411.3028.11.core.png
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=mips-openwrt-linux-musl".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./staging_dir/target-mips_24kc_musl-1.1.16/root-ar71xx/usr/sbin/ddhcpd...done.
[New LWP 3028]
warning: Can't read pathname for load map: Unbekannter Fehler -1.
warning: Could not load shared library symbols for 2 libraries, e.g. /lib/libgcc_s.so.1.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
Core was generated by `/usr/sbin/ddhcpd -D -s 1 -b 2 -c local-node -i br-client -N 10.126.0.0 16 -H /e'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x77766f5c in ?? ()
(gdb) bt
#0 0x77766f5c in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info sharedlibrary
warning: Can't read pathname for load map: Unbekannter Fehler -1.
From To Syms Read Shared Object Library
No /lib/libgcc_s.so.1
No /lib/ld-musl-mips-sf.so.1
(gdb) bt
#0 0x77766f5c in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
I can see that the command line is cut off. I am starting ddhcpd like this:
4508 root 2204 S /usr/sbin/ddhcpd -D -s 1 -b 2 -c local-node -i br-client -N 10.126.0.0 16 -H /etc/ddhcpd.hook -o 51 4 0 0 1 44 -o 3 4 10 126 0 1 -o 6 4 10 126 0 1 -o 54 4 10 126 0 1 -o 1 4 255 255 0 0 -i mmfd0
this is the log up to the crash.
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: network=10.126.0.0/16
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: block_size=4
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: #blocks=16384
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: #spare_blocks=1
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: timeout=60
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: refresh_factor=4
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: tentative_timeout=15
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: client_interface=local-node
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: CONFIG: group_interface=mmfd0
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: ddhcp_block_init(config)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 1)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store(...) -> 1
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 2)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: set_in_option_store( store, code/len: 2/4)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 2)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: set_in_option_store(...) -> append option
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 28)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store(...) -> 28
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 51)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store(...) -> 51
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store( store, code: 54)
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: DEBUG: find_in_option_store(...) -> 54
Sat Jun 23 16:21:48 2018 daemon.err ddhcpd[30989]: INFO: loop timeout: 7500 msecs
The license is only visible in the Debian directory. Please add it to the Readme and to the Github project information.
This would be very helpful for those who have to check licenses in order to incorporate third party software.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.